am 22d9a81f: am 85fdc823: am df171680: am 009ec47d: am 7c761052: am 8114da7b: resolved conflicts for merge of 590e76dd to jb-mr1-dev-plus-aosp

* commit '22d9a81fd5f77f57051ad46d51b085bdc2944e5a':
  DO NOT MERGE: Add a way to get all values of an attribute of DN.
diff --git a/NOTICE b/NOTICE
index 818f6c5..951e506 100644
--- a/NOTICE
+++ b/NOTICE
@@ -32,7 +32,7 @@
    ==  NOTICE file for the ICU License.                                   ==
    =========================================================================
 
-Copyright (c) 1995-2009 International Business Machines Corporation and others
+Copyright (c) 1995-2014 International Business Machines Corporation and others
 
 All rights reserved.
 
@@ -66,250 +66,6 @@
 
 
    =========================================================================
-   ==  NOTICE file for the JUnit License.                                 ==
-   =========================================================================
-
-Common Public License - v 1.0
-
-THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON
-PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF
-THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
-
-1. DEFINITIONS
-
-"Contribution" means:
-
-      a) in the case of the initial Contributor, the initial code and
-         documentation distributed under this Agreement, and
-      b) in the case of each subsequent Contributor:
-
-      i) changes to the Program, and
-
-      ii) additions to the Program;
-
-      where such changes and/or additions to the Program originate
-      from and are distributed by that particular Contributor. A
-      Contribution 'originates' from a Contributor if it was added to
-      the Program by such Contributor itself or anyone acting on such
-      Contributor's behalf. Contributions do not include additions to
-      the Program which: (i) are separate modules of software
-      distributed in conjunction with the Program under their own
-      license agreement, and (ii) are not derivative works of the
-      Program.
-
-"Contributor" means any person or entity that distributes the Program.
-
-"Licensed Patents " mean patent claims licensable by a Contributor
-which are necessarily infringed by the use or sale of its Contribution
-alone or when combined with the Program.
-
-"Program" means the Contributions distributed in accordance with this
-Agreement.
-
-"Recipient" means anyone who receives the Program under this
-Agreement, including all Contributors.
-
-2. GRANT OF RIGHTS
-
-      a) Subject to the terms of this Agreement, each Contributor
-      hereby grants Recipient a non-exclusive, worldwide, royalty-free
-      copyright license to reproduce, prepare derivative works of,
-      publicly display, publicly perform, distribute and sublicense
-      the Contribution of such Contributor, if any, and such
-      derivative works, in source code and object code form.
-
-      b) Subject to the terms of this Agreement, each Contributor
-      hereby grants Recipient a non-exclusive, worldwide, royalty-free
-      patent license under Licensed Patents to make, use, sell, offer
-      to sell, import and otherwise transfer the Contribution of such
-      Contributor, if any, in source code and object code form. This
-      patent license shall apply to the combination of the
-      Contribution and the Program if, at the time the Contribution is
-      added by the Contributor, such addition of the Contribution
-      causes such combination to be covered by the Licensed Patents.
-      The patent license shall not apply to any other combinations
-      which include the Contribution. No hardware per se is licensed
-      hereunder.
-
-      c) Recipient understands that although each Contributor grants
-      the licenses to its Contributions set forth herein, no
-      assurances are provided by any Contributor that the Program does
-      not infringe the patent or other intellectual property rights of
-      any other entity. Each Contributor disclaims any liability to
-      Recipient for claims brought by any other entity based on
-      infringement of intellectual property rights or otherwise. As a
-      condition to exercising the rights and licenses granted
-      hereunder, each Recipient hereby assumes sole responsibility to
-      secure any other intellectual property rights needed, if any.
-      For example, if a third party patent license is required to
-      allow Recipient to distribute the Program, it is Recipient's
-      responsibility to acquire that license before distributing the
-      Program.
-
-      d) Each Contributor represents that to its knowledge it has
-      sufficient copyright rights in its Contribution, if any, to
-      grant the copyright license set forth in this Agreement.
-
-3. REQUIREMENTS
-
-A Contributor may choose to distribute the Program in object code form
-under its own license agreement, provided that:
-
-      a) it complies with the terms and conditions of this Agreement; and
-
-      b) its license agreement:
-
-      i) effectively disclaims on behalf of all Contributors all
-      warranties and conditions, express and implied, including
-      warranties or conditions of title and non-infringement, and
-      implied warranties or conditions of merchantability and fitness
-      for a particular purpose;
-
-      ii) effectively excludes on behalf of all Contributors all
-      liability for damages, including direct, indirect, special,
-      incidental and consequential damages, such as lost profits;
-
-      iii) states that any provisions which differ from this Agreement
-      are offered by that Contributor alone and not by any other
-      party; and
-
-      iv) states that source code for the Program is available from
-      such Contributor, and informs licensees how to obtain it in a
-      reasonable manner on or through a medium customarily used for
-      software exchange.
-
-When the Program is made available in source code form:
-
-      a) it must be made available under this Agreement; and 
-
-      b) a copy of this Agreement must be included with each copy of
-      the Program.
-
-Contributors may not remove or alter any copyright notices contained
-within the Program.
-
-Each Contributor must identify itself as the originator of its
-Contribution, if any, in a manner that reasonably allows subsequent
-Recipients to identify the originator of the Contribution.
-
-4. COMMERCIAL DISTRIBUTION
-
-Commercial distributors of software may accept certain
-responsibilities with respect to end users, business partners and the
-like. While this license is intended to facilitate the commercial use
-of the Program, the Contributor who includes the Program in a
-commercial product offering should do so in a manner which does not
-create potential liability for other Contributors. Therefore, if a
-Contributor includes the Program in a commercial product offering,
-such Contributor ("Commercial Contributor") hereby agrees to defend
-and indemnify every other Contributor ("Indemnified Contributor")
-against any losses, damages and costs (collectively "Losses") arising
-from claims, lawsuits and other legal actions brought by a third party
-against the Indemnified Contributor to the extent caused by the acts
-or omissions of such Commercial Contributor in connection with its
-distribution of the Program in a commercial product offering. The
-obligations in this section do not apply to any claims or Losses
-relating to any actual or alleged intellectual property infringement.
-In order to qualify, an Indemnified Contributor must: a) promptly
-notify the Commercial Contributor in writing of such claim, and b)
-allow the Commercial Contributor to control, and cooperate with the
-Commercial Contributor in, the defense and any related settlement
-negotiations. The Indemnified Contributor may participate in any such
-claim at its own expense.
-
-For example, a Contributor might include the Program in a commercial
-product offering, Product X. That Contributor is then a Commercial
-Contributor. If that Commercial Contributor then makes performance
-claims, or offers warranties related to Product X, those performance
-claims and warranties are such Commercial Contributor's responsibility
-alone. Under this section, the Commercial Contributor would have to
-defend claims against the other Contributors related to those
-performance claims and warranties, and if a court requires any other
-Contributor to pay any damages as a result, the Commercial Contributor
-must pay those damages.
-
-5. NO WARRANTY
-
-EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
-PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
-WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
-OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
-responsible for determining the appropriateness of using and
-distributing the Program and assumes all risks associated with its
-exercise of rights under this Agreement, including but not limited to
-the risks and costs of program errors, compliance with applicable
-laws, damage to or loss of data, programs or equipment, and
-unavailability or interruption of operations.
-
-6. DISCLAIMER OF LIABILITY
-
-EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
-ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
-WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
-DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
-HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. GENERAL
-
-If any provision of this Agreement is invalid or unenforceable under
-applicable law, it shall not affect the validity or enforceability of
-the remainder of the terms of this Agreement, and without further
-action by the parties hereto, such provision shall be reformed to the
-minimum extent necessary to make such provision valid and enforceable.
-
-If Recipient institutes patent litigation against a Contributor with
-respect to a patent applicable to software (including a cross-claim or
-counterclaim in a lawsuit), then any patent licenses granted by that
-Contributor to such Recipient under this Agreement shall terminate as
-of the date such litigation is filed. In addition, if Recipient
-institutes patent litigation against any entity (including a
-cross-claim or counterclaim in a lawsuit) alleging that the Program
-itself (excluding combinations of the Program with other software or
-hardware) infringes such Recipient's patent(s), then such Recipient's
-rights granted under Section 2(b) shall terminate as of the date such
-litigation is filed.
-
-All Recipient's rights under this Agreement shall terminate if it
-fails to comply with any of the material terms or conditions of this
-Agreement and does not cure such failure in a reasonable period of
-time after becoming aware of such noncompliance. If all Recipient's
-rights under this Agreement terminate, Recipient agrees to cease use
-and distribution of the Program as soon as reasonably practicable.
-However, Recipient's obligations under this Agreement and any licenses
-granted by Recipient relating to the Program shall continue and
-survive.
-
-Everyone is permitted to copy and distribute copies of this Agreement,
-but in order to avoid inconsistency the Agreement is copyrighted and
-may only be modified in the following manner. The Agreement Steward
-reserves the right to publish new versions (including revisions) of
-this Agreement from time to time. No one other than the Agreement
-Steward has the right to modify this Agreement. IBM is the initial
-Agreement Steward. IBM may assign the responsibility to serve as the
-Agreement Steward to a suitable separate entity. Each new version of
-the Agreement will be given a distinguishing version number. The
-Program (including Contributions) may always be distributed subject to
-the version of the Agreement under which it was received. In addition,
-after a new version of the Agreement is published, Contributor may
-elect to distribute the Program (including its Contributions) under
-the new version. Except as expressly stated in Sections 2(a) and 2(b)
-above, Recipient receives no rights or licenses to the intellectual
-property of any Contributor under this Agreement, whether expressly,
-by implication, estoppel or otherwise. All rights in the Program not
-expressly granted under this Agreement are reserved.
-
-This Agreement is governed by the laws of the State of New York and
-the intellectual property laws of the United States of America. No
-party to this Agreement will bring a legal action under this Agreement
-more than one year after the cause of action arose. Each party waives
-its rights to a jury trial in any resulting litigation.
-
-
-   =========================================================================
    ==  NOTICE file for the KXML License.                                  ==
    =========================================================================
 
@@ -336,38 +92,6 @@
 
 
    =========================================================================
-   ==  NOTICE file for the SQLite Java Wrapper License.                   ==
-   =========================================================================
-
-This software is copyrighted by Christian Werner <chw@ch-werner.de>
-and others. The following terms apply to all files associated with the
-software unless explicitly disclaimed in individual files.
-
-The authors hereby grant permission to use, copy, modify, distribute,
-and license this software and its documentation for any purpose, provided
-that existing copyright notices are retained in all copies and that this
-notice is included verbatim in any distributions. No written agreement,
-license, or royalty fee is required for any of the authorized uses.
-Modifications to this software may be copyrighted by their authors
-and need not follow the licensing terms described here, provided that
-the new terms are clearly indicated on the first page of each file where
-they apply.
-
-IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
-FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
-ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
-DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
-THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
-INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
-IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
-NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
-MODIFICATIONS.
-
-
-   =========================================================================
    ==  NOTICE file for the W3C License.                                   ==
    =========================================================================
 
diff --git a/NativeCode.mk b/NativeCode.mk
index 12e8114..0ae615e 100644
--- a/NativeCode.mk
+++ b/NativeCode.mk
@@ -66,7 +66,7 @@
 core_c_includes := libcore/include $(LOCAL_C_INCLUDES)
 core_shared_libraries := $(LOCAL_SHARED_LIBRARIES)
 core_static_libraries := $(LOCAL_STATIC_LIBRARIES)
-core_cflags := -Wall -Wextra -Werror
+core_cflags := $(LOCAL_CFLAGS) -Wall -Wextra -Werror
 core_cppflags += -std=gnu++11
 
 core_test_files := \
diff --git a/benchmarks/src/benchmarks/ReferenceGetBenchmark.java b/benchmarks/src/benchmarks/ReferenceGetBenchmark.java
new file mode 100644
index 0000000..80142a1
--- /dev/null
+++ b/benchmarks/src/benchmarks/ReferenceGetBenchmark.java
@@ -0,0 +1,60 @@
+/*
+ * 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
+ */
+
+package benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.*;
+
+public class ReferenceGetBenchmark extends SimpleBenchmark {
+    @Param boolean intrinsicDisabled;
+
+    private Object obj = "str";
+
+    protected void setUp() throws Exception {
+        Field intrinsicDisabledField = Reference.class.getDeclaredField("disableIntrinsic");
+        intrinsicDisabledField.setAccessible(true);
+        intrinsicDisabledField.setBoolean(null, intrinsicDisabled);
+    }
+
+    public void timeSoftReferenceGet(int reps) throws Exception {
+        Reference soft = new SoftReference(obj);
+        for (int i = 0; i < reps; i++) {
+            Object o = soft.get();
+        }
+    }
+
+    public void timeWeakReferenceGet(int reps) throws Exception {
+        Reference weak = new WeakReference(obj);
+        for (int i = 0; i < reps; i++) {
+            Object o = weak.get();
+        }
+    }
+
+    public void timeNonPreservedWeakReferenceGet(int reps) throws Exception {
+        Reference weak = new WeakReference(obj);
+        obj = null;
+        Runtime.getRuntime().gc();
+        for (int i = 0; i < reps; i++) {
+            Object o = weak.get();
+        }
+    }
+}
diff --git a/benchmarks/src/benchmarks/regression/MathBenchmark.java b/benchmarks/src/benchmarks/regression/MathBenchmark.java
index 25a871d..19b2162 100644
--- a/benchmarks/src/benchmarks/regression/MathBenchmark.java
+++ b/benchmarks/src/benchmarks/regression/MathBenchmark.java
@@ -30,339 +30,456 @@
     private final int i = 1;
     private final long l = 1L;
 
-    public void timeAbsD(int reps) {
+    // NOTE: To avoid the benchmarked function from being optimized away, we store the result
+    // and use it as the benchmark's return value. This is good enough for now but may not be in
+    // the future, a smart compiler could determine that the result value will depend on whether
+    // we get into the loop or not and turn the whole loop into an if statement.
+
+    public double timeAbsD(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.abs(d);
+            result = Math.abs(d);
         }
+        return result;
     }
 
-    public void timeAbsF(int reps) {
+    public float timeAbsF(int reps) {
+        float result = f;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.abs(f);
+            result = Math.abs(f);
         }
+        return result;
     }
 
-    public void timeAbsI(int reps) {
+    public int timeAbsI(int reps) {
+        int result = i;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.abs(i);
+            result = Math.abs(i);
         }
+        return result;
     }
 
-    public void timeAbsL(int reps) {
+    public long timeAbsL(int reps) {
+        long result = l;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.abs(l);
+            result = Math.abs(l);
         }
+        return result;
     }
 
-    public void timeAcos(int reps) {
+    public double timeAcos(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.acos(d);
+            result = Math.acos(d);
         }
+        return result;
     }
 
-    public void timeAsin(int reps) {
+    public double timeAsin(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.asin(d);
+            result = Math.asin(d);
         }
+        return result;
     }
 
-    public void timeAtan(int reps) {
+    public double timeAtan(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.atan(d);
+            result = Math.atan(d);
         }
+        return result;
     }
 
-    public void timeAtan2(int reps) {
+    public double timeAtan2(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.atan2(3, 4);
+            result = Math.atan2(3, 4);
         }
+        return result;
     }
 
-    public void timeCbrt(int reps) {
+    public double timeCbrt(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.cbrt(d);
+            result = Math.cbrt(d);
         }
+        return result;
     }
 
-    public void timeCeil(int reps) {
+    public double timeCeil(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.ceil(d);
+            result = Math.ceil(d);
         }
+        return result;
     }
 
-    public void timeCopySignD(int reps) {
+    public double timeCopySignD(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.copySign(d, d);
+            result = Math.copySign(d, d);
         }
+        return result;
     }
 
-    public void timeCopySignF(int reps) {
+    public float timeCopySignF(int reps) {
+        float result = f;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.copySign(f, f);
+            result = Math.copySign(f, f);
         }
+        return result;
     }
 
-    public void timeCopySignD_strict(int reps) {
+    public double timeCopySignD_strict(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            StrictMath.copySign(d, d);
+            result = StrictMath.copySign(d, d);
         }
+        return result;
     }
 
-    public void timeCopySignF_strict(int reps) {
+    public float timeCopySignF_strict(int reps) {
+        float result = f;
         for (int rep = 0; rep < reps; ++rep) {
-            StrictMath.copySign(f, f);
+            result = StrictMath.copySign(f, f);
         }
+        return result;
     }
 
-    public void timeCos(int reps) {
+    public double timeCos(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.cos(d);
+            result = Math.cos(d);
         }
+        return result;
     }
 
-    public void timeCosh(int reps) {
+    public double timeCosh(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.cosh(d);
+            result = Math.cosh(d);
         }
+        return result;
     }
 
-    public void timeExp(int reps) {
+    public double timeExp(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.exp(d);
+            result = Math.exp(d);
         }
+        return result;
     }
 
-    public void timeExpm1(int reps) {
+    public double timeExpm1(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.expm1(d);
+            result = Math.expm1(d);
         }
+        return result;
     }
 
-    public void timeFloor(int reps) {
+    public double timeFloor(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.floor(d);
+            result = Math.floor(d);
         }
+        return result;
     }
 
-    public void timeGetExponentD(int reps) {
+    public int timeGetExponentD(int reps) {
+        int result = i;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.getExponent(d);
+            result = Math.getExponent(d);
         }
+        return result;
     }
 
-    public void timeGetExponentF(int reps) {
+    public int timeGetExponentF(int reps) {
+        int result = i;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.getExponent(f);
+            result = Math.getExponent(f);
         }
+        return result;
     }
 
-    public void timeHypot(int reps) {
+    public double timeHypot(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.hypot(d, d);
+            result = Math.hypot(d, d);
         }
+        return result;
     }
 
-    public void timeIEEEremainder(int reps) {
+    public double timeIEEEremainder(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.IEEEremainder(d, d);
+            result = Math.IEEEremainder(d, d);
         }
+        return result;
     }
 
-    public void timeLog(int reps) {
+    public double timeLog(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.log(d);
+            result = Math.log(d);
         }
+        return result;
     }
 
-    public void timeLog10(int reps) {
+    public double timeLog10(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.log10(d);
+            result = Math.log10(d);
         }
+        return result;
     }
 
-    public void timeLog1p(int reps) {
+    public double timeLog1p(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.log1p(d);
+            result = Math.log1p(d);
         }
+        return result;
     }
 
-    public void timeMaxD(int reps) {
+    public double timeMaxD(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.max(d, d);
+            result = Math.max(d, d);
         }
+        return result;
     }
 
-    public void timeMaxF(int reps) {
+    public float timeMaxF(int reps) {
+        float result = f;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.max(f, f);
+            result = Math.max(f, f);
         }
+        return result;
     }
 
-    public void timeMaxI(int reps) {
+    public int timeMaxI(int reps) {
+        int result = i;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.max(i, i);
+            result = Math.max(i, i);
         }
+        return result;
     }
 
-    public void timeMaxL(int reps) {
+    public long timeMaxL(int reps) {
+        long result = l;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.max(l, l);
+            result = Math.max(l, l);
         }
+        return result;
     }
 
-    public void timeMinD(int reps) {
+    public double timeMinD(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.min(d, d);
+            result = Math.min(d, d);
         }
+        return result;
     }
 
-    public void timeMinF(int reps) {
+    public float timeMinF(int reps) {
+        float result = f;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.min(f, f);
+            result = Math.min(f, f);
         }
+        return result;
     }
 
-    public void timeMinI(int reps) {
+    public int timeMinI(int reps) {
+        int result = i;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.min(i, i);
+            result = Math.min(i, i);
         }
+        return result;
     }
 
-    public void timeMinL(int reps) {
+    public long timeMinL(int reps) {
+        long result = l;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.min(l, l);
+            result = Math.min(l, l);
         }
+        return result;
     }
 
-    public void timeNextAfterD(int reps) {
+    public double timeNextAfterD(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.nextAfter(d, d);
+            result = Math.nextAfter(d, d);
         }
+        return result;
     }
 
-    public void timeNextAfterF(int reps) {
+    public float timeNextAfterF(int reps) {
+        float result = f;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.nextAfter(f, f);
+            result = Math.nextAfter(f, f);
         }
+        return result;
     }
 
-    public void timeNextUpD(int reps) {
+    public double timeNextUpD(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.nextUp(d);
+            result = Math.nextUp(d);
         }
+        return result;
     }
 
-    public void timeNextUpF(int reps) {
+    public float timeNextUpF(int reps) {
+        float result = f;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.nextUp(f);
+            result = Math.nextUp(f);
         }
+        return result;
     }
 
-    public void timePow(int reps) {
+    public double timePow(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.pow(d, d);
+            result = Math.pow(d, d);
         }
+        return result;
     }
 
-    public void timeRandom(int reps) {
+    public double timeRandom(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.random();
+            result = Math.random();
         }
+        return result;
     }
 
-    public void timeRint(int reps) {
+    public double timeRint(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.rint(d);
+            result = Math.rint(d);
         }
+        return result;
     }
 
-    public void timeRoundD(int reps) {
+    public long timeRoundD(int reps) {
+        long result = l;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.round(d);
+            result = Math.round(d);
         }
+        return result;
     }
 
-    public void timeRoundF(int reps) {
+    public int timeRoundF(int reps) {
+        int result = i;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.round(f);
+            result = Math.round(f);
         }
+        return result;
     }
 
-    public void timeScalbD(int reps) {
+    public double timeScalbD(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.scalb(d, 5);
+            result = Math.scalb(d, 5);
         }
+        return result;
     }
 
-    public void timeScalbF(int reps) {
+    public float timeScalbF(int reps) {
+        float result = f;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.scalb(f, 5);
+            result = Math.scalb(f, 5);
         }
+        return result;
     }
 
-    public void timeSignumD(int reps) {
+    public double timeSignumD(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.signum(d);
+            result = Math.signum(d);
         }
+        return result;
     }
 
-    public void timeSignumF(int reps) {
+    public float timeSignumF(int reps) {
+        float result = f;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.signum(f);
+            result = Math.signum(f);
         }
+        return result;
     }
 
-    public void timeSin(int reps) {
+    public double timeSin(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.sin(d);
+            result = Math.sin(d);
         }
+        return result;
     }
 
-    public void timeSinh(int reps) {
+    public double timeSinh(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.sinh(d);
+            result = Math.sinh(d);
         }
+        return result;
     }
 
-    public void timeSqrt(int reps) {
+    public double timeSqrt(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.sqrt(d);
+            result = Math.sqrt(d);
         }
+        return result;
     }
 
-    public void timeTan(int reps) {
+    public double timeTan(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.tan(d);
+            result = Math.tan(d);
         }
+        return result;
     }
 
-    public void timeTanh(int reps) {
+    public double timeTanh(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.tanh(d);
+            result = Math.tanh(d);
         }
+        return result;
     }
 
-    public void timeToDegrees(int reps) {
+    public double timeToDegrees(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.toDegrees(d);
+            result = Math.toDegrees(d);
         }
+        return result;
     }
 
-    public void timeToRadians(int reps) {
+    public double timeToRadians(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.toRadians(d);
+            result = Math.toRadians(d);
         }
+        return result;
     }
 
-    public void timeUlpD(int reps) {
+    public double timeUlpD(int reps) {
+        double result = d;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.ulp(d);
+            result = Math.ulp(d);
         }
+        return result;
     }
 
-    public void timeUlpF(int reps) {
+    public float timeUlpF(int reps) {
+        float result = f;
         for (int rep = 0; rep < reps; ++rep) {
-            Math.ulp(f);
+            result = Math.ulp(f);
         }
+        return result;
     }
 }
diff --git a/benchmarks/src/benchmarks/regression/SSLSocketFactoryBenchmark.java b/benchmarks/src/benchmarks/regression/SSLSocketFactoryBenchmark.java
new file mode 100644
index 0000000..d0448d6
--- /dev/null
+++ b/benchmarks/src/benchmarks/regression/SSLSocketFactoryBenchmark.java
@@ -0,0 +1,29 @@
+/*
+ * 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 com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+import javax.net.ssl.SSLSocketFactory;
+
+public class SSLSocketFactoryBenchmark extends SimpleBenchmark {
+    public void time(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            SSLSocketFactory.getDefault();
+        }
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/DexClassLoader.java b/dalvik/src/main/java/dalvik/system/DexClassLoader.java
index ac2a70a5..a645f42 100644
--- a/dalvik/src/main/java/dalvik/system/DexClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/DexClassLoader.java
@@ -24,9 +24,9 @@
  * installed as part of an application.
  *
  * <p>This class loader requires an application-private, writable directory to
- * cache optimized classes. Use {@code Context.getDir(String, int)} to create
+ * cache optimized classes. Use {@code Context.getCodeCacheDir()} to create
  * such a directory: <pre>   {@code
- *   File dexOutputDir = context.getDir("dex", 0);
+ *   File dexOutputDir = context.getCodeCacheDir();
  * }</pre>
  *
  * <p><strong>Do not cache optimized classes on external storage.</strong>
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
index 5a9c01a..8c78312 100644
--- a/dalvik/src/main/java/dalvik/system/DexFile.java
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -281,21 +281,15 @@
         }
     }
 
-    /*
-     * Open a DEX file.  The value returned is a magic VM cookie.  On
-     * failure, an IOException is thrown.
-     */
-    private static long openDexFile(String sourceName, String outputName, int flags) throws IOException {
-        return openDexFileNative(new File(sourceName).getCanonicalPath(),
-                                 (outputName == null) ? null : new File(outputName).getCanonicalPath(),
-                                 flags);
-    }
-
     private static native void closeDexFile(long cookie);
     private static native Class defineClassNative(String name, ClassLoader loader, long cookie)
             throws ClassNotFoundException, NoClassDefFoundError;
     private static native String[] getClassNameList(long cookie);
-    private static native long openDexFileNative(String sourceName, String outputName, int flags);
+    /*
+     * Open a DEX file.  The value returned is a magic VM cookie.  On
+     * failure, an IOException is thrown.
+     */
+    private static native long openDexFile(String sourceName, String outputName, int flags);
 
     /**
      * Returns true if the VM believes that the apk/jar file is out of date
diff --git a/dex/src/main/java/com/android/dex/DexException.java b/dex/src/main/java/com/android/dex/DexException.java
index a30a46f..ee0af18 100644
--- a/dex/src/main/java/com/android/dex/DexException.java
+++ b/dex/src/main/java/com/android/dex/DexException.java
@@ -22,7 +22,7 @@
  * Thrown when there's a format problem reading, writing, or generally
  * processing a dex file.
  */
-public final class DexException extends ExceptionWithContext {
+public class DexException extends ExceptionWithContext {
     public DexException(String message) {
         super(message);
     }
diff --git a/dex/src/main/java/com/android/dex/DexIndexOverflowException.java b/dex/src/main/java/com/android/dex/DexIndexOverflowException.java
new file mode 100644
index 0000000..3226207
--- /dev/null
+++ b/dex/src/main/java/com/android/dex/DexIndexOverflowException.java
@@ -0,0 +1,30 @@
+/*
+ * 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 com.android.dex;
+
+/**
+ * Thrown when there's an index overflow writing a dex file.
+ */
+public final class DexIndexOverflowException extends DexException {
+    public DexIndexOverflowException(String message) {
+        super(message);
+    }
+
+    public DexIndexOverflowException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/expectations/brokentests.txt b/expectations/brokentests.txt
index 5b5803d..5cebb63 100644
--- a/expectations/brokentests.txt
+++ b/expectations/brokentests.txt
@@ -98,21 +98,6 @@
   ]
 },
 {
-  description: "Some DecimalFormat tests fail, treating tests as broken while investigate further.",
-  bug: 12781028,
-  result: EXEC_FAILED,
-  names: [
-      "org.apache.harmony.tests.java.text.DecimalFormatTest#testSerializationSelf",
-      "org.apache.harmony.tests.java.text.DecimalFormatTest#test_formatD",
-      "org.apache.harmony.tests.java.text.DecimalFormatTest#test_formatDLjava_lang_StringBufferLjava_text_FieldPosition",
-      "org.apache.harmony.tests.java.text.DecimalFormatTest#test_formatDLjava_lang_StringBufferLjava_text_FieldPosition_problem_cases",
-      "org.apache.harmony.tests.java.text.DecimalFormatTest#test_formatD_2",
-      "org.apache.harmony.tests.java.text.DecimalFormatTest#test_formatJLjava_lang_StringBufferLjava_text_FieldPosition",
-      "org.apache.harmony.tests.java.text.DecimalFormatTest#test_formatToCharacterIterator_very_large",
-      "org.apache.harmony.tests.java.text.DecimalFormatTest#test_format_minus_zero"
-  ]
-},
-{
   description: "Fails in CTS, passes in CoreTestRunner.",
   result: EXEC_FAILED,
   names: [
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index 0924bd4..94b1a59 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -1374,89 +1374,37 @@
   ]
 },
 {
-  description: "Known failures in klp-modular-dev branch being suppressed for dory / molly.",
-  bug: 14674275,
-  names: [
-    "libcore.java.lang.SystemTest#testArrayCopyConcurrentModification",
-    "libcore.java.lang.ref.FinalizeTest#testSystemRunFinalizationReturnsEvenIfQueueIsNonEmpty",
-    "libcore.java.lang.reflect.ClassLoaderReflectionTest#testConstructorsOfDifferentClassLoadersAreNotEqual",
-    "libcore.java.lang.reflect.ClassLoaderReflectionTest#testFieldsOfDifferentClassLoadersAreNotEqual",
-    "libcore.java.lang.reflect.MethodTest#testEqualMethodEqualsAndHashCode",
-    "libcore.java.lang.reflect.MethodTest#testHashCodeSpec",
-    "libcore.java.lang.reflect.ProxyTest#testDeclaredExceptionIntersectedByExactReturnTypes",
-    "libcore.java.lang.reflect.ProxyTest#testReturnTypeDoesNotSatisfyAllConstraintsWithLenientCaller",
-    "libcore.java.net.ConcurrentCloseTest#test_connect",
-    "libcore.java.net.ConcurrentCloseTest#test_connect_nonBlocking",
-    "libcore.java.net.ConcurrentCloseTest#test_connect_timeout",
-    "libcore.java.net.InetAddressTest#test_isReachable",
-    "libcore.java.net.OldCookieHandlerTest#test_get_put",
-    "libcore.java.net.OldSocketTest#test_ConstructorLjava_lang_StringILjava_net_InetAddressI2",
-    "libcore.java.net.OldSocketTest#test_connectLjava_net_SocketAddressI",
-    "libcore.java.net.URLConnectionTest#testConnectViaHttpProxyToHttpsUsingBadProxyAndHttpResponseCache",
-    "libcore.java.util.prefs.OldFilePreferencesImplTest#testSystemChildNodes",
-    "libcore.java.util.prefs.OldNodeChangeEventTest#testGetChild",
-    "libcore.java.util.prefs.OldNodeChangeEventTest#testGetParent",
-    "org.apache.harmony.luni.tests.java.net.URLConnectionTest#test_getLastModified",
-    "org.apache.harmony.tests.java.io.SerializationStressTest4#test_writeObject_Proxy",
-    "org.apache.harmony.tests.java.lang.RuntimeTest#test_gc",
-    "org.apache.harmony.tests.java.lang.ref.ReferenceQueueTest#test_removeJ",
-    "org.apache.harmony.tests.java.lang.reflect.FieldTest#testProtectedFieldAccess",
-    "org.apache.harmony.tests.java.lang.reflect.ProxyTest#test_ProxyClass_withParentAndSubInThrowList",
-    "org.apache.harmony.tests.java.net.DatagramSocketTest#test_setBroadcastZ",
-    "org.apache.harmony.tests.java.net.JarURLConnectionTest#test_getURLEncodedEntry",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_joinGroupLjava_net_InetAddress_IPv4",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_joinGroupLjava_net_InetAddress_IPv6",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv4",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv6",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins_IPv4",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins_IPv6",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_leaveGroupLjava_net_InetAddress_IPv4",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_leaveGroupLjava_net_InetAddress_IPv6",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv4",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv6",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_sendLjava_net_DatagramPacketB_IPv4",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_sendLjava_net_DatagramPacketB_IPv6",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_setLoopbackModeSendReceive_IPv4",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_setLoopbackModeSendReceive_IPv6",
-    "org.apache.harmony.tests.java.net.MulticastSocketTest#test_setNetworkInterfaceLjava_net_NetworkInterface_IPv4",
-    "org.apache.harmony.tests.java.text.SimpleDateFormatTest#test_formatLjava_util_DateLjava_lang_StringBufferLjava_text_FieldPosition",
-    "org.apache.harmony.tests.java.text.SimpleDateFormatTest#test_parseLjava_lang_StringLjava_text_ParsePosition",
-    "tests.api.internal.net.www.protocol.file.FileURLConnectionTest#testGetContentType",
-    "tests.api.internal.net.www.protocol.file.FileURLConnectionTest#testGetInputStream",
-    "tests.api.internal.net.www.protocol.file.FileURLConnectionTest#testHeaderFunctions",
-    "tests.api.internal.net.www.protocol.file.FileURLConnectionTest#testHeader_BoundaryCheck"
-  ]
-},
-{
   description: "Known failure in GregorianCalendarTest",
   bug: 12778197,
   name: "org.apache.harmony.tests.java.util.GregorianCalendarTest#test_computeTime"
 },
 {
-  description: "Environment specific Console test suppressed for dory / molly",
-  bug: 12491103,
-  name: "org.apache.harmony.tests.java.io.ConsoleTest#test_readPassword_LString_LObject"
-},
-{
-  description: "Some tests take too long on clockwork devices. Suppressed on klp-modular-dev.",
-  bug: 5513723,
+  description: "SpdyConnection issue https://github.com/square/okhttp/issues/644 crashes the test app. Android does not provide SPDY/HTTP_2 connections by default so have been suppressed.",
+  bug: 14462336,
   names: [
-    "libcore.java.security.KeyPairGeneratorTest#test_getInstance_provider0",
-    "libcore.java.security.KeyPairGeneratorTest#test_getInstance_provider1",
-    "libcore.java.security.KeyPairGeneratorTest#test_getInstance_provider2",
-    "libcore.java.security.KeyPairGeneratorTest#test_getInstance_provider3",
-    "libcore.java.security.KeyPairGeneratorTest#test_getInstance_provider4",
-    "libcore.java.security.KeyPairGeneratorTest#test_getInstance_provider5",
-    "libcore.java.security.KeyPairGeneratorTest#test_getInstance_provider6",
-    "libcore.java.security.KeyPairGeneratorTest#test_getInstance_provider7",
-    "libcore.java.security.KeyPairGeneratorTest#test_getInstance_provider8",
-    "libcore.java.security.KeyPairGeneratorTest#test_getInstance_provider9"
+    "com.squareup.okhttp.internal.spdy.SpdyConnectionTest",
+    "com.squareup.okhttp.internal.http.HttpOverHttp20Draft09Test",
+    "com.squareup.okhttp.internal.http.HttpOverSpdy3Test",
+    "com.squareup.okhttp.internal.http.URLConnectionTest#npnSetsProtocolHeader_SPDY_3",
+    "com.squareup.okhttp.internal.http.URLConnectionTest#npnSetsProtocolHeader_HTTP_2",
+    "com.squareup.okhttp.internal.http.URLConnectionTest#zeroLengthPost_SPDY_3",
+    "com.squareup.okhttp.internal.http.URLConnectionTest#zeroLengthPost_HTTP_2",
+    "com.squareup.okhttp.internal.http.URLConnectionTest#zeroLengthPut_SPDY_3",
+    "com.squareup.okhttp.internal.http.URLConnectionTest#zeroLengthPut_HTTP_2"
   ]
 },
 {
-  description: "Suppression of a test that proves there is a known bug with Matcher",
-  bug: 14865710,
-  name: "org.apache.harmony.tests.java.util.ScannerParseLargeFileBenchmarkTest#testParseLargeFile"
+  description: "Okhttp test hardcodes the TLS version expected.",
+  bug: 14462336,
+  names: [
+    "com.squareup.okhttp.internal.http.URLConnectionTest#sslFallbackNotUsedWhenRecycledConnectionFails"
+  ]
+},
+{
+  description: "The test relies on SimpleDateFormat zzz producing GMT not GMT+00:00 as it does on Android. Android issue 66136.",
+  bug: 14462336,
+  names: [
+    "com.squareup.okhttp.internal.http.HttpResponseCacheTest#setIfModifiedSince"
+  ]
 }
 ]
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java
index a029a51..959f83c 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/InetAddressTest.java
@@ -349,9 +349,8 @@
         } catch (IllegalArgumentException e) {
             // correct
         }
-        // tests unreachable address. 192.0.2.1 is reserved by RFC 5737
-        // and should not be used outside of example code / docs.
-        ia = Inet4Address.getByName("192.0.2.1");
+        // tests nowhere
+        ia = Inet4Address.getByName("1.1.1.1");
         assertFalse(ia.isReachable(1000));
         assertFalse(ia.isReachable(null, 0, 1000));
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/CollationElementIteratorTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/CollationElementIteratorTest.java
index 0ca489c..081b446 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/CollationElementIteratorTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/CollationElementIteratorTest.java
@@ -125,7 +125,8 @@
 
   public void testGetMaxExpansion() {
     String text = "cha";
-    RuleBasedCollator rbColl = (RuleBasedCollator) Collator.getInstance(new Locale("es", "", "TRADITIONAL"));
+    RuleBasedCollator rbColl = (RuleBasedCollator) Collator.getInstance(
+            Locale.forLanguageTag("es-u-co-trad"));
     CollationElementIterator iterator = rbColl.getCollationElementIterator(text);
     int order = iterator.next();
     while (order != CollationElementIterator.NULLORDER) {
@@ -177,7 +178,8 @@
   }
 
   public void testSetOffset() {
-    RuleBasedCollator rbColl = (RuleBasedCollator) Collator.getInstance(new Locale("es", "", "TRADITIONAL"));
+    RuleBasedCollator rbColl = (RuleBasedCollator) Collator.getInstance(
+            Locale.forLanguageTag("es-u-co-trad"));
     String text = "cha";
     CollationElementIterator iterator = rbColl.getCollationElementIterator(text);
     iterator.setOffset(0);
@@ -189,7 +191,8 @@
   }
 
   public void testSetTextString() {
-    RuleBasedCollator rbColl = (RuleBasedCollator) Collator.getInstance(new Locale("es", "", "TRADITIONAL"));
+    RuleBasedCollator rbColl = (RuleBasedCollator) Collator.getInstance(
+            Locale.forLanguageTag("es-u-co-trad"));
     String text = "caa";
     CollationElementIterator iterator = rbColl.getCollationElementIterator(text);
     iterator.setOffset(0);
@@ -208,7 +211,8 @@
   }
 
   public void testSetTextCharacterIterator() {
-    RuleBasedCollator rbColl = (RuleBasedCollator) Collator.getInstance(new Locale("es", "", "TRADITIONAL"));
+    RuleBasedCollator rbColl = (RuleBasedCollator) Collator.getInstance(
+            Locale.forLanguageTag("es-u-co-trad"));
     String text = "caa";
     CollationElementIterator iterator = rbColl.getCollationElementIterator(text);
     iterator.setOffset(1);
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java
index 9fe3681..70e41a2 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatSymbolsTest.java
@@ -88,7 +88,7 @@
 
         Locale locale = new Locale("not exist language", "not exist country");
         DateFormatSymbols symbols = DateFormatSymbols.getInstance(locale);
-        assertNotNull(symbols);
+        assertEquals(DateFormatSymbols.getInstance(Locale.ROOT), symbols);
     }
 
     /**
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/RuleBasedCollatorTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/RuleBasedCollatorTest.java
index f5a8057..906857b 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/RuleBasedCollatorTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/RuleBasedCollatorTest.java
@@ -105,7 +105,7 @@
 
   public void testGetCollationElementIteratorString() throws Exception {
     {
-      Locale locale = new Locale("es", "", "TRADITIONAL");
+      Locale locale = Locale.forLanguageTag("es-u-co-trad");
       RuleBasedCollator coll = (RuleBasedCollator) Collator.getInstance(locale);
       String source = "cha";
       CollationElementIterator iterator = coll.getCollationElementIterator(source);
@@ -147,7 +147,7 @@
 
   public void testGetCollationElementIteratorCharacterIterator() throws Exception {
     {
-      Locale locale = new Locale("es", "", "TRADITIONAL");
+      Locale locale = Locale.forLanguageTag("es-u-co-trad");
       RuleBasedCollator coll = (RuleBasedCollator) Collator.getInstance(locale);
       String text = "cha";
       StringCharacterIterator source = new StringCharacterIterator(text);
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CollectionsTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CollectionsTest.java
index ed25241..a1629d2 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CollectionsTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CollectionsTest.java
@@ -23,8 +23,9 @@
 import tests.support.Support_ListTest;
 import tests.support.Support_SetTest;
 import tests.support.Support_UnmodifiableCollectionTest;
-import tests.support.Support_UnmodifiableMapTest;
+
 import java.io.Serializable;
+import java.lang.reflect.Array;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayDeque;
@@ -38,6 +39,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
@@ -67,7 +69,7 @@
 
     private HashMap hm;
 
-    private Object[] objArray;
+    private Integer[] objArray;
 
     private Object[] myobjArray;
 
@@ -125,10 +127,10 @@
             colSize = c.size();
             normalCountingList = new ArrayList(colSize);
             offsetCountingList = new ArrayList(colSize);
-            for (int counter = 0; counter < colSize; counter++)
-                normalCountingList.add(new Integer(counter));
-            for (int counter = 0; counter < colSize; counter++)
-                offsetCountingList.add(new Integer(counter + colSize));
+            for (int i = 0; i < colSize; i++)
+                normalCountingList.add(new Integer(i));
+            for (int i = 0; i < colSize; i++)
+                offsetCountingList.add(new Integer(i + colSize));
             col.clear();
             if (offset)
                 col.addAll(offsetCountingList);
@@ -208,12 +210,12 @@
             mapSize = m.size();
             normalCountingMap = new HashMap(mapSize);
             offsetCountingMap = new HashMap(mapSize);
-            for (int counter = 0; counter < mapSize; counter++) {
-                myInt = new Integer(counter);
+            for (int i = 0; i < mapSize; i++) {
+                myInt = new Integer(i);
                 normalCountingMap.put(myInt, myInt);
             }
-            for (int counter = 0; counter < mapSize; counter++) {
-                myInt = new Integer(counter + mapSize);
+            for (int i = 0; i < mapSize; i++) {
+                myInt = new Integer(i + mapSize);
                 offsetCountingMap.put(myInt, myInt);
             }
             map.clear();
@@ -241,21 +243,6 @@
         }
     }
 
-    public static class CollectionTest extends junit.framework.TestCase {
-
-        Collection col; // must contain the Integers 0 to 99
-
-        public CollectionTest(String p1) {
-            super(p1);
-        }
-
-        public CollectionTest(String p1, Collection c) {
-            super(p1);
-            col = c;
-        }
-
-    }
-
     static class MyInt {
         int data;
 
@@ -268,10 +255,6 @@
         }
     }
 
-    /**
-     * java.util.Collections#binarySearch(java.util.List,
-     *        java.lang.Object)
-     */
     public void test_binarySearchLjava_util_ListLjava_lang_Object() {
         // Test for method int
         // java.util.Collections.binarySearch(java.util.List, java.lang.Object)
@@ -284,17 +267,13 @@
         } catch (NullPointerException e) {
             //Expected
         }
-        for (int counter = 0; counter < llSize; counter++) {
+        for (int i = 0; i < llSize; i++) {
             assertEquals("Returned incorrect binary search item position", ll
-                    .get(counter), ll.get(Collections.binarySearch(ll, ll
-                    .get(counter))));
+                    .get(i), ll.get(Collections.binarySearch(ll, ll
+                    .get(i))));
         }
     }
 
-    /**
-     * java.util.Collections#binarySearch(java.util.List,
-     *        java.lang.Object, java.util.Comparator)
-     */
     public void test_binarySearchLjava_util_ListLjava_lang_ObjectLjava_util_Comparator() {
         // Test for method int
         // java.util.Collections.binarySearch(java.util.List, java.lang.Object,
@@ -310,12 +289,12 @@
         } catch (NullPointerException e) {
             //Expected
         }
-        for (int counter = 0; counter < rSize; counter++) {
+        for (int i = 0; i < rSize; i++) {
             assertEquals(
                     "Returned incorrect binary search item position using custom comparator",
-                    myReversedLinkedList.get(counter), myReversedLinkedList
+                    myReversedLinkedList.get(i), myReversedLinkedList
                     .get(Collections.binarySearch(myReversedLinkedList,
-                            myReversedLinkedList.get(counter), comp)));
+                            myReversedLinkedList.get(i), comp)));
         }
     }
 
@@ -327,9 +306,6 @@
         }
     }
 
-    /**
-     * java.util.Collections#copy(java.util.List, java.util.List)
-     */
     public void test_copyLjava_util_ListLjava_util_List() {
         // Test for method void java.util.Collections.copy(java.util.List,
         // java.util.List)
@@ -355,9 +331,9 @@
         al.add(extraElement);
         al.add(extraElement2);
         Collections.copy(al, ll);
-        for (int counter = 0; counter < llSize; counter++) {
+        for (int i = 0; i < llSize; i++) {
             assertEquals("Elements do not match after copying collection", ll
-                    .get(counter), al.get(counter));
+                    .get(i), al.get(i));
         }
         assertTrue("Elements after copied elements affected by copy",
                 extraElement == al.get(llSize)
@@ -399,9 +375,6 @@
         }
     }
 
-    /**
-     * java.util.Collections#copy(java.util.List, java.util.List)
-     */
     public void test_copy_check_index() {
         ArrayList a1 = new ArrayList();
         a1.add("one");
@@ -420,9 +393,6 @@
         assertEquals("aa", a2.get(0));
     }
 
-    /**
-     * java.util.Collections#enumeration(java.util.Collection)
-     */
     public void test_enumerationLjava_util_Collection() {
         // Test for method java.util.Enumeration
         // java.util.Collections.enumeration(java.util.Collection)
@@ -438,9 +408,6 @@
                 count);
     }
 
-    /**
-     * java.util.Collections#fill(java.util.List, java.lang.Object)
-     */
     public void test_fillLjava_util_ListLjava_lang_Object() {
         // Test for method void java.util.Collections.fill(java.util.List,
         // java.lang.Object)
@@ -476,9 +443,6 @@
         }
     }
 
-    /**
-     * java.util.Collections#max(java.util.Collection)
-     */
     public void test_maxLjava_util_Collection() {
         // Test for method java.lang.Object
         // java.util.Collections.max(java.util.Collection)
@@ -507,10 +471,6 @@
         }
     }
 
-    /**
-     * java.util.Collections#max(java.util.Collection,
-     *        java.util.Comparator)
-     */
     public void test_maxLjava_util_CollectionLjava_util_Comparator() {
         // Test for method java.lang.Object
         // java.util.Collections.max(java.util.Collection, java.util.Comparator)
@@ -523,9 +483,6 @@
                 myobjArray[0]);
     }
 
-    /**
-     * java.util.Collections#min(java.util.Collection)
-     */
     public void test_minLjava_util_Collection() {
         // Test for method java.lang.Object
         // java.util.Collections.min(java.util.Collection)
@@ -534,10 +491,6 @@
                 objArray[0]);
     }
 
-    /**
-     * java.util.Collections#min(java.util.Collection,
-     *        java.util.Comparator)
-     */
     public void test_minLjava_util_CollectionLjava_util_Comparator() {
         // Test for method java.lang.Object
         // java.util.Collections.min(java.util.Collection, java.util.Comparator)
@@ -550,16 +503,13 @@
                 myobjArray[objArray.length - 1]);
     }
 
-    /**
-     * java.util.Collections#nCopies(int, java.lang.Object)
-     */
     public void test_nCopiesILjava_lang_Object() {
         // Test for method java.util.List java.util.Collections.nCopies(int,
         // java.lang.Object)
         Object o = new Object();
         List l = Collections.nCopies(100, o);
-        Iterator i = l.iterator();
-        Object first = i.next();
+        Iterator iterator = l.iterator();
+        Object first = iterator.next();
         assertEquals("Returned list consists of copies not refs", first, o);
         assertEquals("Returned list of incorrect size", 100, l.size());
         assertTrue("Contains", l.contains(o));
@@ -569,10 +519,10 @@
         assertTrue("null nCopies contains null", Collections.nCopies(2, null)
                 .contains(null));
         l = Collections.nCopies(20, null);
-        i = l.iterator();
-        for (int counter = 0; i.hasNext(); counter++) {
-            assertTrue("List is too large", counter < 20);
-            assertNull("Element should be null: " + counter, i.next());
+        iterator = l.iterator();
+        for (int i = 0; iterator.hasNext(); i++) {
+            assertTrue("List is too large", i < 20);
+            assertNull("Element should be null: " + i, iterator.next());
         }
         try {
             l.add(o);
@@ -588,9 +538,6 @@
         }
     }
 
-    /**
-     * java.util.Collections#reverse(java.util.List)
-     */
     public void test_reverseLjava_util_List() {
         // Test for method void java.util.Collections.reverse(java.util.List)
         try {
@@ -617,9 +564,6 @@
                 + myList.get(1), myList.get(1));
     }
 
-    /**
-     * java.util.Collections#reverseOrder()
-     */
     public void test_reverseOrder() {
         // Test for method java.util.Comparator
         // java.util.Collections.reverseOrder()
@@ -628,14 +572,11 @@
         LinkedList list2 = new LinkedList(ll);
         Collections.sort(list2, comp);
         final int llSize = ll.size();
-        for (int counter = 0; counter < llSize; counter++)
+        for (int i = 0; i < llSize; i++)
             assertEquals("New comparator does not reverse sorting order", list2
-                    .get(llSize - counter - 1), ll.get(counter));
+                    .get(llSize - i - 1), ll.get(i));
     }
 
-    /**
-     * java.util.Collections#shuffle(java.util.List)
-     */
     public void test_shuffleLjava_util_List() {
         // Test for method void java.util.Collections.shuffle(java.util.List)
         // Assumes ll is sorted and has no duplicate keys and is large ( > 20
@@ -682,15 +623,15 @@
         else
             Collections.shuffle(list, new Random(200));
 
-        for (int counter = 0; counter < size - 1; counter++) {
-            if (((Integer) list.get(counter)).compareTo((Integer) list.get(counter + 1)) > 0) {
+        for (int i = 0; i < size - 1; i++) {
+            if (((Integer) list.get(i)).compareTo((Integer) list.get(i + 1)) > 0) {
                 sorted = false;
             }
         }
         assertFalse("Shuffling sorted " + type
                 + " list resulted in sorted list (should be unlikely)", sorted);
-        for (int counter = 0; counter < 20; counter++) {
-            index = 30031 * counter % (size + 1); // 30031 is a large prime
+        for (int i = 0; i < 20; i++) {
+            index = 30031 * i % (size + 1); // 30031 is a large prime
             if (list.get(index) != ll.get(index))
                 allMatch = false;
         }
@@ -698,9 +639,6 @@
                 + " list", allMatch);
     }
 
-    /**
-     * java.util.Collections#shuffle(java.util.List, java.util.Random)
-     */
     public void test_shuffleLjava_util_ListLjava_util_Random() {
         // Test for method void java.util.Collections.shuffle(java.util.List,
         // java.util.Random)
@@ -731,9 +669,6 @@
         assertEquals("acb", l.get(0).toString() + l.get(1) + l.get(2));
     }
 
-    /**
-     * java.util.Collections#singleton(java.lang.Object)
-     */
     public void test_singletonLjava_lang_Object() {
         // Test for method java.util.Set
         // java.util.Collections.singleton(java.lang.Object)
@@ -754,9 +689,6 @@
         }
     }
 
-    /**
-     * java.util.Collections#sort(java.util.List)
-     */
     public void test_sortLjava_util_List() {
         // Test for method void java.util.Collections.sort(java.util.List)
         // assumes no duplicate keys in ll
@@ -771,22 +703,19 @@
         Collections.shuffle(ll);
         Collections.sort(ll);
         Collections.sort(reversedLinkedList);
-        for (int counter = 0; counter < llSize - 1; counter++) {
+        for (int i = 0; i < llSize - 1; i++) {
             assertTrue(
                     "Sorting shuffled list resulted in unsorted list",
-                    ((Integer) ll.get(counter)).compareTo((Integer) ll.get(counter + 1)) < 0);
+                    ((Integer) ll.get(i)).compareTo((Integer) ll.get(i + 1)) < 0);
         }
 
-        for (int counter = 0; counter < rllSize - 1; counter++) {
+        for (int i = 0; i < rllSize - 1; i++) {
             assertTrue("Sorting reversed list resulted in unsorted list",
-                    ((Integer) reversedLinkedList.get(counter))
-                            .compareTo((Integer) reversedLinkedList.get(counter + 1)) < 0);
+                    ((Integer) reversedLinkedList.get(i))
+                            .compareTo((Integer) reversedLinkedList.get(i + 1)) < 0);
         }
     }
 
-    /**
-     * java.util.Collections#sort(java.util.List, java.util.Comparator)
-     */
     public void test_sortLjava_util_ListLjava_util_Comparator() {
         // Test for method void java.util.Collections.sort(java.util.List,
         // java.util.Comparator)
@@ -801,11 +730,11 @@
         Collections.sort(myll, comp);
         final int llSize = myll.size();
 
-        for (int counter = 0; counter < llSize - 1; counter++) {
+        for (int i = 0; i < llSize - 1; i++) {
             assertTrue(
                     "Sorting shuffled list with custom comparator resulted in unsorted list",
-                    ((MyInt) myll.get(counter)).compareTo((MyInt) myll
-                            .get(counter + 1)) >= 0);
+                    ((MyInt) myll.get(i)).compareTo((MyInt) myll
+                            .get(i + 1)) >= 0);
         }
 
         ArrayList al = new ArrayList();
@@ -834,9 +763,6 @@
         }
     }
 
-    /**
-     * java.util.Collections#swap(java.util.List, int, int)
-     */
     public void test_swapLjava_util_ListII() {
         // Test for method swap(java.util.List, int, int)
 
@@ -897,10 +823,6 @@
         }
     }
 
-    /**
-     * java.util.Collections#replaceAll(java.util.List, java.lang.Object,
-     *        java.lang.Object)
-     */
     public void test_replaceAllLjava_util_ListLjava_lang_ObjectLjava_lang_Object() {
         // Test for method replaceAll(java.util.List, java.lang.Object,
         // java.lang.Object)
@@ -998,9 +920,6 @@
         }
     }
 
-    /**
-     * java.util.Collections#rotate(java.util.List, int)
-     */
     public void test_rotateLjava_util_ListI() {
         // Test for method rotate(java.util.List, int)
 
@@ -1081,9 +1000,6 @@
         return buffer.toString();
     }
 
-    /**
-     * java.util.Collections#rotate(java.util.List, int)
-     */
     public void test_rotate2() {
         List list = new ArrayList();
         try {
@@ -1112,10 +1028,6 @@
                 (String) list.get(4));
     }
 
-    /**
-     * java.util.Collections#indexOfSubList(java.util.List,
-     *        java.util.List)
-     */
     public void test_indexOfSubListLjava_util_ListLjava_util_List() {
         // Test for method int indexOfSubList(java.util.List, java.util.List)
         List list = new ArrayList();
@@ -1146,10 +1058,6 @@
         testwithCharList(8, "", "SUBLIST", true);
     }
 
-    /**
-     * java.util.Collections#indexOfSubList(java.util.List,
-     *        java.util.List)
-     */
     public void test_indexOfSubList2() {
         ArrayList sub = new ArrayList();
         sub.add(new Integer(1));
@@ -1205,7 +1113,6 @@
                 .indexOfSubList(src, sub2));
     }
 
-
     private void testwithCharList(int count, String string1, String string2,
             boolean first) {
         char[] chars = string1.toCharArray();
@@ -1229,10 +1136,6 @@
                     sublist));
     }
 
-    /**
-     * java.util.Collections#lastIndexOfSubList(java.util.List,
-     *        java.util.List)
-     */
     public void test_lastIndexOfSubListLjava_util_ListLjava_util_List() {
         // Test for method int lastIndexOfSubList(java.util.List,
         // java.util.List)
@@ -1264,10 +1167,6 @@
         testwithCharList(8, "", "SUBLIST", false);
     }
 
-    /**
-     * java.util.Collections#lastIndexOfSubList(java.util.List,
-     *        java.util.List)
-     */
     public void test_lastIndexOfSubList2() {
         ArrayList sub = new ArrayList();
         sub.add(new Integer(1));
@@ -1337,9 +1236,6 @@
                 Collections.lastIndexOfSubList(src, sub2));
     }
 
-    /**
-     * java.util.Collections#list(java.util.Enumeration)
-     */
     public void test_listLjava_util_Enumeration() {
         // Test for method java.util.ArrayList list(java.util.Enumeration)
 
@@ -1355,9 +1251,6 @@
         }
     }
 
-    /**
-     * java.util.Collections#synchronizedCollection(java.util.Collection)
-     */
     public void test_synchronizedCollectionLjava_util_Collection() {
         // Test for method java.util.Collection
         // java.util.Collections.synchronizedCollection(java.util.Collection)
@@ -1415,9 +1308,6 @@
         assertTrue("should contain self ref", synchCol.toString().indexOf("(this") > -1);
     }
 
-    /**
-     * java.util.Collections#synchronizedList(java.util.List)
-     */
     public void test_synchronizedListLjava_util_List() {
         try {
             Collections.synchronizedList(null);
@@ -1505,9 +1395,6 @@
                 synchList.get(25));
     }
 
-    /**
-     * java.util.Collections#synchronizedMap(java.util.Map)
-     */
     public void test_synchronizedMapLjava_util_Map() {
         // Test for method java.util.Map
         // java.util.Collections.synchronizedMap(java.util.Map)
@@ -1561,7 +1448,7 @@
             smallMap.put(objArray[i].toString(), objArray[i]);
         }
         synchMap = Collections.synchronizedMap(smallMap);
-        new Support_UnmodifiableMapTest("", synchMap).runTest();
+        new MapTestSupport(synchMap).runTest();
         synchMap.keySet().remove(objArray[50].toString());
         assertNull(
                 "Removing a key from the keySet of the synchronized map did not remove it from the synchronized map: ",
@@ -1571,9 +1458,17 @@
                 smallMap.get(objArray[50].toString()));
     }
 
-    /**
-     * java.util.Collections#synchronizedSet(java.util.Set)
-     */
+    public void test_unmodifiableMap_LinkedHashMap() {
+        // LinkedHashMap has a well defined iteration order and shows ordering issues with
+        // entrySet() / keySet() methods: iterator(), toArray(T[]) and toArray(). See bug 72073.
+        LinkedHashMap<String, Integer> smallMap = new LinkedHashMap<String, Integer>();
+        for (int i = 0; i < 100; i++) {
+            Integer object = objArray[i];
+            smallMap.put(object.toString(), object);
+        }
+        new MapTestSupport(smallMap).runTest();
+    }
+
     public void test_synchronizedSetLjava_util_Set() {
         // Test for method java.util.Set
         // java.util.Collections.synchronizedSet(java.util.Set)
@@ -1631,9 +1526,6 @@
         assertTrue("should contain self ref", mySet.toString().indexOf("(this") > -1);
     }
 
-    /**
-     * java.util.Collections#synchronizedSortedMap(java.util.SortedMap)
-     */
     public void test_synchronizedSortedMapLjava_util_SortedMap() {
         // Test for method java.util.SortedMap
         // java.util.Collections.synchronizedSortedMap(java.util.SortedMap)
@@ -1679,7 +1571,7 @@
             smallMap.put(objArray[i].toString(), objArray[i]);
         }
         synchMap = Collections.synchronizedSortedMap(smallMap);
-        new Support_UnmodifiableMapTest("", synchMap).runTest();
+        new MapTestSupport(synchMap).runTest();
         synchMap.keySet().remove(objArray[50].toString());
         assertNull(
                 "Removing a key from the keySet of the synchronized map did not remove it from the synchronized map",
@@ -1689,9 +1581,6 @@
                 smallMap.get(objArray[50].toString()));
     }
 
-    /**
-     * java.util.Collections#synchronizedSortedSet(java.util.SortedSet)
-     */
     public void test_synchronizedSortedSetLjava_util_SortedSet() {
         // Test for method java.util.SortedSet
         // java.util.Collections.synchronizedSortedSet(java.util.SortedSet)
@@ -1733,9 +1622,6 @@
         }
     }
 
-    /**
-     * java.util.Collections#unmodifiableCollection(java.util.Collection)
-     */
     public void test_unmodifiableCollectionLjava_util_Collection() {
         // Test for method java.util.Collection
         // java.util.Collections.unmodifiableCollection(java.util.Collection)
@@ -1743,9 +1629,9 @@
         Collection c = Collections.unmodifiableCollection(ll);
         assertTrue("Returned collection is of incorrect size", c.size() == ll
                 .size());
-        Iterator i = ll.iterator();
-        while (i.hasNext())
-            assertTrue("Returned list missing elements", c.contains(i.next()));
+        Iterator iterator = ll.iterator();
+        while (iterator.hasNext())
+            assertTrue("Returned list missing elements", c.contains(iterator.next()));
         try {
             c.add(new Object());
         } catch (UnsupportedOperationException e) {
@@ -1772,16 +1658,13 @@
                 .contains(new Integer(20)));
 
         myCollection = new ArrayList();
-        for (int counter = 0; counter < 100; counter++) {
-            myCollection.add(objArray[counter]);
+        for (int i = 0; i < 100; i++) {
+            myCollection.add(objArray[i]);
         }
         new Support_UnmodifiableCollectionTest("", Collections
                 .unmodifiableCollection(myCollection)).runTest();
     }
 
-    /**
-     * java.util.Collections#unmodifiableList(java.util.List)
-     */
     public void test_unmodifiableListLjava_util_List() {
         // Test for method java.util.List
         // java.util.Collections.unmodifiableList(java.util.List)
@@ -1801,9 +1684,9 @@
                 "Returned List should not implement Random Access interface",
                 !(c instanceof RandomAccess));
 
-        Iterator i = ll.iterator();
-        while (i.hasNext())
-            assertTrue("Returned list missing elements", c.contains(i.next()));
+        Iterator iterator = ll.iterator();
+        while (iterator.hasNext())
+            assertTrue("Returned list missing elements", c.contains(iterator.next()));
         try {
             c.add(new Object());
         } catch (UnsupportedOperationException e) {
@@ -1833,8 +1716,8 @@
                 c instanceof RandomAccess);
 
         smallList = new ArrayList();
-        for (int counter = 0; counter < 100; counter++) {
-            smallList.add(objArray[counter]);
+        for (int i = 0; i < 100; i++) {
+            smallList.add(objArray[i]);
         }
         List myList = Collections.unmodifiableList(smallList);
         assertTrue("List should not contain null", !myList.contains(null));
@@ -1845,25 +1728,22 @@
         assertTrue("get failed on unmodifiable list", myList.get(50).equals(
                 new Integer(50)));
         ListIterator listIterator = myList.listIterator();
-        for (int counter = 0; listIterator.hasNext(); counter++) {
+        for (int i = 0; listIterator.hasNext(); i++) {
             assertTrue("List has wrong elements", ((Integer) listIterator
-                    .next()).intValue() == counter);
+                    .next()).intValue() == i);
         }
         new Support_UnmodifiableCollectionTest("", smallList).runTest();
     }
 
-    /**
-     * java.util.Collections#unmodifiableMap(java.util.Map)
-     */
     public void test_unmodifiableMapLjava_util_Map() {
         // Test for method java.util.Map
         // java.util.Collections.unmodifiableMap(java.util.Map)
         boolean exception = false;
         Map c = Collections.unmodifiableMap(hm);
         assertTrue("Returned map is of incorrect size", c.size() == hm.size());
-        Iterator i = hm.keySet().iterator();
-        while (i.hasNext()) {
-            Object x = i.next();
+        Iterator iterator = hm.keySet().iterator();
+        while (iterator.hasNext()) {
+            Object x = iterator.next();
             assertTrue("Returned map missing elements", c.get(x).equals(
                     hm.get(x)));
         }
@@ -1885,8 +1765,8 @@
         assertTrue("Allowed modification of map", exception);
 
         exception = false;
-        Iterator it = c.entrySet().iterator();
-        Map.Entry entry = (Map.Entry) it.next();
+        Iterator entrySetIterator = c.entrySet().iterator();
+        Map.Entry entry = (Map.Entry) entrySetIterator.next();
         try {
             entry.setValue("modified");
         } catch (UnsupportedOperationException e) {
@@ -1927,26 +1807,21 @@
                 .equals(new Long(30)));
 
         smallMap = new HashMap();
-        for (int counter = 0; counter < 100; counter++) {
-            smallMap.put(objArray[counter].toString(), objArray[counter]);
+        for (int i = 0; i < 100; i++) {
+            smallMap.put(objArray[i].toString(), objArray[i]);
         }
-        unmodMap = Collections.unmodifiableMap(smallMap);
-        new Support_UnmodifiableMapTest("", unmodMap).runTest();
-
+        new MapTestSupport(smallMap).runTest();
     }
 
-    /**
-     * java.util.Collections#unmodifiableSet(java.util.Set)
-     */
     public void test_unmodifiableSetLjava_util_Set() {
         // Test for method java.util.Set
         // java.util.Collections.unmodifiableSet(java.util.Set)
         boolean exception = false;
         Set c = Collections.unmodifiableSet(s);
         assertTrue("Returned set is of incorrect size", c.size() == s.size());
-        Iterator i = ll.iterator();
-        while (i.hasNext())
-            assertTrue("Returned set missing elements", c.contains(i.next()));
+        Iterator iterator = ll.iterator();
+        while (iterator.hasNext())
+            assertTrue("Returned set missing elements", c.contains(iterator.next()));
         try {
             c.add(new Object());
         } catch (UnsupportedOperationException e) {
@@ -1969,16 +1844,13 @@
         assertTrue("Should contain null", mySet.contains(null));
 
         mySet = new TreeSet();
-        for (int counter = 0; counter < 100; counter++) {
-            mySet.add(objArray[counter]);
+        for (int i = 0; i < 100; i++) {
+            mySet.add(objArray[i]);
         }
         new Support_UnmodifiableCollectionTest("", Collections
                 .unmodifiableSet(mySet)).runTest();
     }
 
-    /**
-     * java.util.Collections#unmodifiableSortedMap(java.util.SortedMap)
-     */
     public void test_unmodifiableSortedMapLjava_util_SortedMap() {
         // Test for method java.util.SortedMap
         // java.util.Collections.unmodifiableSortedMap(java.util.SortedMap)
@@ -2012,9 +1884,6 @@
         fail("Allowed modification of map");
     }
 
-    /**
-     * java.util.Collections#unmodifiableSortedSet(java.util.SortedSet)
-     */
     public void test_unmodifiableSortedSetLjava_util_SortedSet() {
         // Test for method java.util.SortedSet
         // java.util.Collections.unmodifiableSortedSet(java.util.SortedSet)
@@ -2115,10 +1984,6 @@
         }
     }
 
-
-    /**
-     * java.util.Collections#checkType(Object, Class)
-     */
     public void test_checkType_Ljava_lang_Object_Ljava_lang_Class() throws Exception {
         Method m = Collections.class.getDeclaredMethod("checkType", Object.class, Class.class);
         m.setAccessible(true);
@@ -2257,9 +2122,6 @@
         SerializationTest.verifySelf(set);
     }
 
-    /**
-     * {@link java.util.Collections#asLifoQueue(Deque)
-     */
     public void test_asLifoQueue() throws Exception {
         Integer testInt[] = new Integer[100];
         Integer test101 = new Integer(101);
@@ -2300,7 +2162,6 @@
     @SuppressWarnings({ "unchecked", "boxing" })
     public void testSerializationSelf_asLifoQueue() throws Exception {
         Integer testInt[] = new Integer[100];
-        Integer test101 = new Integer(101);
         for (int i = 0; i < testInt.length; i++) {
             testInt[i] = new Integer(i);
         }
@@ -2320,23 +2181,18 @@
         });
     }
 
-    /**
-     * java.util.Collections#emptyList()
-     */
     public void test_emptyList() {
         List<String> list = Collections.emptyList();
         assertTrue("should be true", list.isEmpty());
     }
 
-    /**
-     * Sets up the fixture, for example, open a network connection. This method
-     * is called before a test is executed.
-     */
-    protected void setUp() {
-        objArray = new Object[1000];
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        objArray = new Integer[1000];
         myobjArray = new Object[1000];
         for (int i = 0; i < objArray.length; i++) {
-            objArray[i] = new Integer(i);
+            objArray[i] = i;
             myobjArray[i] = new MyInt(i);
         }
 
@@ -2360,23 +2216,170 @@
     }
 
     /**
-     * Tears down the fixture, for example, close a network connection. This
-     * method is called after a test is executed.
+     * A class shared by various Map-related tests that checks the properties and contents of a
+     * supplied Map and compares the some methods to the same map when wrapped with
+     * {@link Collections#unmodifiableMap(java.util.Map)}.
      */
-    protected void tearDown() {
-        objArray = null;
-        myobjArray = null;
+    static class MapTestSupport {
 
-        ll = null;
-        myll = null;
-        reversedLinkedList = null;
-        myReversedLinkedList = null;
-        s = null;
-        mys = null;
-        hm = null;
-    }
+        // must be a map containing the string keys "0"-"99" paired with the Integer
+        // values Integer(0) to Integer(99)
+        private final Map<String, Integer> modifiableMap;
+        private final Map<String, Integer> unmodifiableMap;
 
-    protected void doneSuite() {
-        objArray = null;
+        public MapTestSupport(Map<String, Integer> modifiableMap) {
+            this.modifiableMap = modifiableMap;
+            unmodifiableMap = Collections.unmodifiableMap(modifiableMap);
+        }
+
+        public void runTest() {
+            testContents(modifiableMap);
+            testContents(unmodifiableMap);
+
+            // values()
+            new Support_UnmodifiableCollectionTest("values() from map test", modifiableMap.values())
+                    .runTest();
+            new Support_UnmodifiableCollectionTest("values() from unmodifiable map test",
+                    unmodifiableMap.values()).runTest();
+
+            // entrySet()
+            testEntrySet(modifiableMap.entrySet(), unmodifiableMap.entrySet());
+
+            // keySet()
+            testKeySet(modifiableMap.keySet(), unmodifiableMap.keySet());
+        }
+
+        private static void testContents(Map<String, Integer> map) {
+            // size
+            assertTrue("Size should return 100, returned: " + map.size(), map.size() == 100);
+
+            // containsKey
+            assertTrue("Should contain the key \"0\"", map.containsKey("0"));
+            assertTrue("Should contain the key \"50\"", map.containsKey("50"));
+            assertTrue("Should not contain the key \"100\"", !map.containsKey("100"));
+
+            // containsValue
+            assertTrue("Should contain the value 0", map.containsValue(0));
+            assertTrue("Should contain the value 50", map.containsValue(50));
+            assertTrue("Should not contain value 100", !map.containsValue(100));
+
+            // get
+            assertTrue("getting \"0\" didn't return 0", map.get("0") == 0);
+            assertTrue("getting \"50\" didn't return 50", map.get("50") == 50);
+            assertNull("getting \"100\" didn't return null", map.get("100"));
+
+            // isEmpty
+            assertTrue("should have returned false to isEmpty", !map.isEmpty());
+        }
+
+        private static void testEntrySet(
+                Set<Map.Entry<String, Integer>> referenceEntrySet,
+                Set<Map.Entry<String, Integer>> entrySet) {
+            // entrySet should be a set of mappings {"0", 0}, {"1",1}... {"99", 99}
+            assertEquals(100, referenceEntrySet.size());
+            assertEquals(100, entrySet.size());
+
+            // The ordering may be undefined for a map implementation but the ordering must be the
+            // same across iterator(), toArray() and toArray(T[]) for a given map *and* the same for the
+            // modifiable and unmodifiable map.
+            crossCheckOrdering(referenceEntrySet, entrySet, Map.Entry.class);
+        }
+
+        private static void testKeySet(Set<String> referenceKeySet, Set<String> keySet) {
+            // keySet should be a set of the strings "0" to "99"
+            testKeySetContents(referenceKeySet);
+            testKeySetContents(keySet);
+
+            // The ordering may be undefined for a map implementation but the ordering must be the
+            // same across iterator(), toArray() and toArray(T[]) for a given map *and* the same for the
+            // modifiable and unmodifiable map.
+            crossCheckOrdering(referenceKeySet, keySet, String.class);
+        }
+
+        private static void testKeySetContents(Set<String> keySet) {
+            // contains
+            assertTrue("should contain \"0\"", keySet.contains("0"));
+            assertTrue("should contain \"50\"", keySet.contains("50"));
+            assertTrue("should not contain \"100\"", !keySet.contains("100"));
+
+            // containsAll
+            HashSet<String> hs = new HashSet<String>();
+            hs.add("0");
+            hs.add("25");
+            hs.add("99");
+            assertTrue("Should contain set of \"0\", \"25\", and \"99\"", keySet.containsAll(hs));
+            hs.add("100");
+            assertTrue("Should not contain set of \"0\", \"25\", \"99\" and \"100\"",
+                    !keySet.containsAll(hs));
+
+            // isEmpty
+            assertTrue("Should not be empty", !keySet.isEmpty());
+
+            // size
+            assertEquals("Returned wrong size.", 100, keySet.size());
+        }
+
+        private static <T> void crossCheckOrdering(Set<T> set1, Set<T> set2, Class<?> elementType) {
+            Iterator<T> set1Iterator = set1.iterator();
+            Iterator<T> set2Iterator = set2.iterator();
+
+            T[] zeroLengthArray = createArray(elementType, 0);
+            T[] set1TypedArray1 = set1.toArray(zeroLengthArray);
+            assertEquals(set1.size(), set1TypedArray1.length);
+
+            // Compare set1.iterator(), set2.iterator() and set1.toArray(new T[0])
+            int entryCount = 0;
+            while (set1Iterator.hasNext()) {
+                T set1Entry = set1Iterator.next();
+                T set2Entry = set2Iterator.next();
+
+                // Compare set1 with set2
+                assertEquals(set1Entry, set2Entry);
+
+                // Compare the iterator with the array. The arrays will be checked against each other.
+                assertEquals(set1Entry, set1TypedArray1[entryCount]);
+
+                entryCount++;
+            }
+            assertFalse(set2Iterator.hasNext());
+            assertEquals(set1.size(), entryCount);
+
+            // Compare the various arrays with each other.
+
+            // set1.toArray(new T[size])
+            T[] parameterArray1 = createArray(elementType, set1.size());
+            T[] set1TypedArray2 = set1.toArray(parameterArray1);
+            assertSame(set1TypedArray2, parameterArray1);
+            assertArrayEquals(set1TypedArray1, set1TypedArray2);
+
+            // set1.toArray()
+            Object[] set1UntypedArray = set1.toArray();
+            assertEquals(set1.size(), set1UntypedArray.length);
+            assertArrayEquals(set1TypedArray1, set1UntypedArray);
+
+            // set2.toArray(new T[0])
+            T[] set2TypedArray1 = set2.toArray(zeroLengthArray);
+            assertEquals(set1.size(), set2TypedArray1.length);
+            assertArrayEquals(set1TypedArray1, set2TypedArray1);
+
+            // set2.toArray(new T[size])
+            T[] parameterArray2 = createArray(elementType, set2.size());
+            T[] set2TypedArray2 = set1.toArray(parameterArray2);
+            assertSame(set2TypedArray2, parameterArray2);
+            assertArrayEquals(set1TypedArray1, set1TypedArray2);
+
+            // set2.toArray()
+            Object[] set2UntypedArray = set2.toArray();
+            assertArrayEquals(set1TypedArray1, set2UntypedArray);
+        }
+
+        private static <T> void assertArrayEquals(T[] array1, T[] array2) {
+            assertTrue(Arrays.equals(array1, array2));
+        }
+
+        @SuppressWarnings("unchecked")
+        private static <T> T[] createArray(Class<?> elementType, int size) {
+            return (T[]) Array.newInstance(elementType, size);
+        }
     }
 }
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 740340e..d5d8191 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
@@ -23,6 +23,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
+import java.security.CodeSigner;
 import java.security.Permission;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
@@ -98,6 +99,8 @@
 
     private static final String INVALID_CHAIN_JAR = "hyts_signed_invalidChain.jar";
 
+    private static final String AMBIGUOUS_SIGNERS_JAR = "hyts_signed_ambiguousSignerArray.jar";
+
     private File resources;
 
     // custom security manager
@@ -647,13 +650,18 @@
                         + jarName + "\"", foundCerts);
     }
 
-    private Certificate[] getSignedJarCerts(String jarName, boolean chainCheck) throws Exception {
+    private static class Results {
+        public Certificate[] certificates;
+        public CodeSigner[] signers;
+    }
+
+    private Results getSignedJarCerts(String jarName) throws Exception {
         Support_Resources.copyFile(resources, null, jarName);
 
         File file = new File(resources, jarName);
-        Certificate[] foundCerts = null;
+        Results results = new Results();
 
-        JarFile jarFile = new JarFile(file, true, ZipFile.OPEN_READ, chainCheck);
+        JarFile jarFile = new JarFile(file, true, ZipFile.OPEN_READ);
         try {
 
             Enumeration<JarEntry> e = jarFile.entries();
@@ -664,8 +672,10 @@
                 is.skip(entry.getSize());
                 is.close();
                 Certificate[] certs = entry.getCertificates();
+                CodeSigner[] signers = entry.getCodeSigners();
                 if (certs != null && certs.length > 0) {
-                    foundCerts = certs;
+                    results.certificates = certs;
+                    results.signers = signers;
                     break;
                 }
             }
@@ -673,42 +683,38 @@
             jarFile.close();
         }
 
-        return foundCerts;
+        return results;
     }
 
-    public void testJarFile_Signed_ValidChain_NoCheck() throws Exception {
-        Certificate[] certs = getSignedJarCerts(VALID_CHAIN_JAR, false);
-        assertNotNull(certs);
-        assertEquals(Arrays.deepToString(certs), 3, certs.length);
-        assertEquals("CN=fake-chain", ((X509Certificate) certs[0]).getSubjectDN().toString());
-        assertEquals("CN=intermediate1", ((X509Certificate) certs[1]).getSubjectDN().toString());
-        assertEquals("CN=root1", ((X509Certificate) certs[2]).getSubjectDN().toString());
+    public void testJarFile_Signed_ValidChain() throws Exception {
+        Results result = getSignedJarCerts(VALID_CHAIN_JAR);
+        assertNotNull(result);
+        assertEquals(Arrays.deepToString(result.certificates), 3, result.certificates.length);
+        assertEquals(Arrays.deepToString(result.signers), 1, result.signers.length);
+        assertEquals(3, result.signers[0].getSignerCertPath().getCertificates().size());
+        assertEquals("CN=fake-chain", ((X509Certificate) result.certificates[0]).getSubjectDN().toString());
+        assertEquals("CN=intermediate1", ((X509Certificate) result.certificates[1]).getSubjectDN().toString());
+        assertEquals("CN=root1", ((X509Certificate) result.certificates[2]).getSubjectDN().toString());
     }
 
-    public void testJarFile_Signed_ValidChain_Check() throws Exception {
-        Certificate[] certs = getSignedJarCerts(VALID_CHAIN_JAR, true);
-        assertNotNull(certs);
-        assertEquals(Arrays.deepToString(certs), 3, certs.length);
-        assertEquals("CN=fake-chain", ((X509Certificate) certs[0]).getSubjectDN().toString());
-        assertEquals("CN=intermediate1", ((X509Certificate) certs[1]).getSubjectDN().toString());
-        assertEquals("CN=root1", ((X509Certificate) certs[2]).getSubjectDN().toString());
+    public void testJarFile_Signed_InvalidChain() throws Exception {
+        Results result = getSignedJarCerts(INVALID_CHAIN_JAR);
+        assertNotNull(result);
+        assertEquals(Arrays.deepToString(result.certificates), 3, result.certificates.length);
+        assertEquals(Arrays.deepToString(result.signers), 1, result.signers.length);
+        assertEquals(3, result.signers[0].getSignerCertPath().getCertificates().size());
+        assertEquals("CN=fake-chain", ((X509Certificate) result.certificates[0]).getSubjectDN().toString());
+        assertEquals("CN=intermediate1", ((X509Certificate) result.certificates[1]).getSubjectDN().toString());
+        assertEquals("CN=root1", ((X509Certificate) result.certificates[2]).getSubjectDN().toString());
     }
 
-    public void testJarFile_Signed_InvalidChain_NoCheck() throws Exception {
-        Certificate[] certs = getSignedJarCerts(INVALID_CHAIN_JAR, false);
-        assertNotNull(certs);
-        assertEquals(Arrays.deepToString(certs), 3, certs.length);
-        assertEquals("CN=fake-chain", ((X509Certificate) certs[0]).getSubjectDN().toString());
-        assertEquals("CN=intermediate1", ((X509Certificate) certs[1]).getSubjectDN().toString());
-        assertEquals("CN=root1", ((X509Certificate) certs[2]).getSubjectDN().toString());
-    }
-
-    public void testJarFile_Signed_InvalidChain_Check() throws Exception {
-        Certificate[] certs = getSignedJarCerts(INVALID_CHAIN_JAR, true);
-        assertNotNull(certs);
-        assertEquals(Arrays.deepToString(certs), 2, certs.length);
-        assertEquals("CN=fake-chain", ((X509Certificate) certs[0]).getSubjectDN().toString());
-        assertEquals("CN=intermediate1", ((X509Certificate) certs[1]).getSubjectDN().toString());
+    public void testJarFile_Signed_AmbiguousSigners() throws Exception {
+        Results result = getSignedJarCerts(AMBIGUOUS_SIGNERS_JAR);
+        assertNotNull(result);
+        assertEquals(Arrays.deepToString(result.certificates), 2, result.certificates.length);
+        assertEquals(Arrays.deepToString(result.signers), 2, result.signers.length);
+        assertEquals(1, result.signers[0].getSignerCertPath().getCertificates().size());
+        assertEquals(1, result.signers[1].getSignerCertPath().getCertificates().size());
     }
 
     /*
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
index 48b2dfa..a905c71 100644
--- a/libart/src/main/java/dalvik/system/VMRuntime.java
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -39,6 +39,7 @@
         ABI_TO_INSTRUCTION_SET_MAP.put("armeabi", "arm");
         ABI_TO_INSTRUCTION_SET_MAP.put("armeabi-v7a", "arm");
         ABI_TO_INSTRUCTION_SET_MAP.put("mips", "mips");
+        ABI_TO_INSTRUCTION_SET_MAP.put("mips64", "mips64");
         ABI_TO_INSTRUCTION_SET_MAP.put("x86", "x86");
         ABI_TO_INSTRUCTION_SET_MAP.put("x86_64", "x86_64");
         ABI_TO_INSTRUCTION_SET_MAP.put("arm64-v8a", "arm64");
@@ -325,4 +326,14 @@
 
         return instructionSet;
     }
+
+    public static boolean is64BitInstructionSet(String instructionSet) {
+        return "arm64".equals(instructionSet) ||
+                "x86_64".equals(instructionSet) ||
+                "mips64".equals(instructionSet);
+    }
+
+    public static boolean is64BitAbi(String abi) {
+        return is64BitInstructionSet(getInstructionSet(abi));
+    }
 }
diff --git a/libart/src/main/java/java/lang/ref/Reference.java b/libart/src/main/java/java/lang/ref/Reference.java
index 3b03ff1..31ea588 100644
--- a/libart/src/main/java/java/lang/ref/Reference.java
+++ b/libart/src/main/java/java/lang/ref/Reference.java
@@ -98,6 +98,22 @@
 public abstract class Reference<T> {
 
     /**
+     * Forces JNI path.
+     * If GC is not in progress (ie: not going through slow path), the referent
+     * can be quickly returned through intrinsic without passing through JNI.
+     * This flag forces the JNI path so that it can be tested and benchmarked.
+     */
+    private static boolean disableIntrinsic = false;
+
+    /**
+     * Slow path flag for the reference processor.
+     * 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.
+     */
+    private static boolean slowPathEnabled = false;
+
+    /**
      * The object to which this reference refers.
      * VM requirement: this field <em>must</em> be called "referent"
      * and be an object.
diff --git a/libdvm/src/main/java/dalvik/system/VMRuntime.java b/libdvm/src/main/java/dalvik/system/VMRuntime.java
index 1d58d8d..f117b70 100644
--- a/libdvm/src/main/java/dalvik/system/VMRuntime.java
+++ b/libdvm/src/main/java/dalvik/system/VMRuntime.java
@@ -370,4 +370,12 @@
 
         return instructionSet;
     }
+
+    public static boolean is64BitInstructionSet(String instructionSet) {
+        return false;
+    }
+
+    public static boolean is64BitAbi(String abi) {
+        return false;
+    }
 }
diff --git a/luni/src/main/java/android/system/ErrnoException.java b/luni/src/main/java/android/system/ErrnoException.java
index 134d6a0..90155c89 100644
--- a/luni/src/main/java/android/system/ErrnoException.java
+++ b/luni/src/main/java/android/system/ErrnoException.java
@@ -24,8 +24,6 @@
  * A checked exception thrown when {@link Os} methods fail. This exception contains the native
  * errno value, for comparison against the constants in {@link OsConstants}, should sophisticated
  * callers need to adjust their behavior based on the exact failure.
- *
- * @hide
  */
 public final class ErrnoException extends Exception {
   private final String functionName;
diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java
index e7613df..0b80b52 100644
--- a/luni/src/main/java/android/system/Os.java
+++ b/luni/src/main/java/android/system/Os.java
@@ -47,8 +47,6 @@
  * primitives used to implement the higher-level APIs.
  *
  * <p>The corresponding constants can be found in {@link OsConstants}.
- *
- * @hide
  */
 public final class Os {
   private Os() {}
@@ -63,6 +61,8 @@
    */
   public static boolean access(String path, int mode) throws ErrnoException { return Libcore.os.access(path, mode); }
 
+  /** @hide */ public static InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException { return Libcore.os.android_getaddrinfo(node, hints, netId); }
+
   /**
    * See <a href="http://man7.org/linux/man-pages/man2/bind.2.html">bind(2)</a>.
    */
@@ -157,8 +157,6 @@
    */
   public static String gai_strerror(int error) { return Libcore.os.gai_strerror(error); }
 
-  /** @hide */ public static InetAddress[] getaddrinfo(String node, StructAddrinfo hints) throws GaiException { return Libcore.os.getaddrinfo(node, hints); }
-
   /**
    * See <a href="http://man7.org/linux/man-pages/man2/getegid.2.html">getegid(2)</a>.
    */
diff --git a/luni/src/main/java/android/system/OsConstants.java b/luni/src/main/java/android/system/OsConstants.java
index cfed2f6..c758eb7 100644
--- a/luni/src/main/java/android/system/OsConstants.java
+++ b/luni/src/main/java/android/system/OsConstants.java
@@ -18,7 +18,6 @@
 
 /**
  * Constants and helper functions for use with {@link Os}.
- * @hide
  */
 public final class OsConstants {
     private OsConstants() {
@@ -352,6 +351,8 @@
     public static final int POLLRDNORM = placeholder();
     public static final int POLLWRBAND = placeholder();
     public static final int POLLWRNORM = placeholder();
+    public static final int PR_GET_DUMPABLE = placeholder();
+    public static final int PR_SET_DUMPABLE = placeholder();
     public static final int PR_SET_NO_NEW_PRIVS = placeholder();
     public static final int PROT_EXEC = placeholder();
     public static final int PROT_NONE = placeholder();
diff --git a/luni/src/main/java/android/system/StructPollfd.java b/luni/src/main/java/android/system/StructPollfd.java
index 8bdecb2..b812612 100644
--- a/luni/src/main/java/android/system/StructPollfd.java
+++ b/luni/src/main/java/android/system/StructPollfd.java
@@ -22,8 +22,6 @@
 /**
  * Used as an in/out parameter to {@link Os#poll}.
  * Corresponds to C's {@code struct pollfd} from {@code &lt;poll.h&gt;}.
- *
- * @hide
  */
 public final class StructPollfd {
   /** The file descriptor to poll. */
diff --git a/luni/src/main/java/android/system/StructStat.java b/luni/src/main/java/android/system/StructStat.java
index 87bd50c..a6958c1 100644
--- a/luni/src/main/java/android/system/StructStat.java
+++ b/luni/src/main/java/android/system/StructStat.java
@@ -21,8 +21,6 @@
 /**
  * File information returned by {@link Os#fstat}, {@link Os#lstat}, and {@link Os#stat}.
  * Corresponds to C's {@code struct stat} from {@code &lt;stat.h&gt;}.
- *
- * @hide
  */
 public final class StructStat {
   /** Device ID of device containing file. */
diff --git a/luni/src/main/java/android/system/StructStatVfs.java b/luni/src/main/java/android/system/StructStatVfs.java
index b0b7802..942a39a 100644
--- a/luni/src/main/java/android/system/StructStatVfs.java
+++ b/luni/src/main/java/android/system/StructStatVfs.java
@@ -20,8 +20,6 @@
 
 /**
  * File information returned by {@link Os#fstatvfs} and {@link Os#statvfs}.
- *
- * @hide
  */
 public final class StructStatVfs {
   /** File system block size (used for block counts). */
diff --git a/luni/src/main/java/android/system/StructUtsname.java b/luni/src/main/java/android/system/StructUtsname.java
index c62dbfa..5d9127b 100644
--- a/luni/src/main/java/android/system/StructUtsname.java
+++ b/luni/src/main/java/android/system/StructUtsname.java
@@ -21,8 +21,6 @@
 /**
  * Information returned by {@link Os#uname}.
  * Corresponds to C's {@code struct utsname} from {@code &lt;sys/utsname.h&gt;}.
- *
- * @hide
  */
 public final class StructUtsname {
   /** The OS name, such as "Linux". */
diff --git a/luni/src/main/java/android/util/MutableBoolean.java b/luni/src/main/java/android/util/MutableBoolean.java
index 90bf68c..5a8a200 100644
--- a/luni/src/main/java/android/util/MutableBoolean.java
+++ b/luni/src/main/java/android/util/MutableBoolean.java
@@ -17,7 +17,6 @@
 package android.util;
 
 /**
- * @hide
  */
 public final class MutableBoolean {
   public boolean value;
diff --git a/luni/src/main/java/android/util/MutableByte.java b/luni/src/main/java/android/util/MutableByte.java
index 65738b9..7397ba4 100644
--- a/luni/src/main/java/android/util/MutableByte.java
+++ b/luni/src/main/java/android/util/MutableByte.java
@@ -17,7 +17,6 @@
 package android.util;
 
 /**
- * @hide
  */
 public final class MutableByte {
   public byte value;
diff --git a/luni/src/main/java/android/util/MutableChar.java b/luni/src/main/java/android/util/MutableChar.java
index b59bab3..f435331 100644
--- a/luni/src/main/java/android/util/MutableChar.java
+++ b/luni/src/main/java/android/util/MutableChar.java
@@ -17,7 +17,6 @@
 package android.util;
 
 /**
- * @hide
  */
 public final class MutableChar {
   public char value;
diff --git a/luni/src/main/java/android/util/MutableDouble.java b/luni/src/main/java/android/util/MutableDouble.java
index 3e2cc3a..f62f47e 100644
--- a/luni/src/main/java/android/util/MutableDouble.java
+++ b/luni/src/main/java/android/util/MutableDouble.java
@@ -17,7 +17,6 @@
 package android.util;
 
 /**
- * @hide
  */
 public final class MutableDouble {
   public double value;
diff --git a/luni/src/main/java/android/util/MutableFloat.java b/luni/src/main/java/android/util/MutableFloat.java
index 6e30501..6b5441c 100644
--- a/luni/src/main/java/android/util/MutableFloat.java
+++ b/luni/src/main/java/android/util/MutableFloat.java
@@ -17,7 +17,6 @@
 package android.util;
 
 /**
- * @hide
  */
 public final class MutableFloat {
   public float value;
diff --git a/luni/src/main/java/android/util/MutableInt.java b/luni/src/main/java/android/util/MutableInt.java
index 8220c44..2f93030 100644
--- a/luni/src/main/java/android/util/MutableInt.java
+++ b/luni/src/main/java/android/util/MutableInt.java
@@ -17,7 +17,6 @@
 package android.util;
 
 /**
- * @hide
  */
 public final class MutableInt {
   public int value;
diff --git a/luni/src/main/java/android/util/MutableLong.java b/luni/src/main/java/android/util/MutableLong.java
index 5df6a0d..94beab5 100644
--- a/luni/src/main/java/android/util/MutableLong.java
+++ b/luni/src/main/java/android/util/MutableLong.java
@@ -17,7 +17,6 @@
 package android.util;
 
 /**
- * @hide
  */
 public final class MutableLong {
   public long value;
diff --git a/luni/src/main/java/android/util/MutableShort.java b/luni/src/main/java/android/util/MutableShort.java
index 3880fef..cdd9923 100644
--- a/luni/src/main/java/android/util/MutableShort.java
+++ b/luni/src/main/java/android/util/MutableShort.java
@@ -17,7 +17,6 @@
 package android.util;
 
 /**
- * @hide
  */
 public final class MutableShort {
   public short value;
diff --git a/luni/src/main/java/java/lang/StringToReal.java b/luni/src/main/java/java/lang/StringToReal.java
index 0f4eb09..4bd4fb1 100644
--- a/luni/src/main/java/java/lang/StringToReal.java
+++ b/luni/src/main/java/java/lang/StringToReal.java
@@ -122,20 +122,20 @@
                     result.e = -result.e;
                 }
             } catch (NumberFormatException ex) {
-                // We already checked the string, so the exponent must have been out of range for an int.
+                // We already checked the string, so the exponent must have been out of range for an
+                // int.
                 if (negativeExponent) {
                     result.zero = true;
                 } else {
                     result.infinity = true;
                 }
-                return result;
+                // Fall through: We want to check the content of the mantissa and throw an
+                // exception if it contains invalid characters. For example: "JUNK" * 10^12 should
+                // be treated as an error, not as infinity.
             }
         } else {
             end = length;
         }
-        if (length == 0) {
-            throw invalidReal(s, isDouble);
-        }
 
         int start = 0;
         c = s.charAt(start);
@@ -151,7 +151,19 @@
             throw invalidReal(s, isDouble);
         }
 
-        int decimal = s.indexOf('.');
+        // Confirm that the mantissa should parse.
+        int decimal = -1;
+        for (int i = start; i < end; i++) {
+            char mc = s.charAt(i);
+            if (mc == '.') {
+                if (decimal != -1) {
+                    throw invalidReal(s, isDouble);
+                }
+                decimal = i;
+            } else if (mc < '0' || mc > '9') {
+                throw invalidReal(s, isDouble);
+            }
+        }
         if (decimal > -1) {
             result.e -= end - decimal - 1;
             s = s.substring(start, decimal) + s.substring(decimal + 1, end);
@@ -159,10 +171,17 @@
             s = s.substring(start, end);
         }
 
-        if ((length = s.length()) == 0) {
+        length = s.length();
+        if (length == 0) {
             throw invalidReal(s, isDouble);
         }
 
+        // All syntactic checks that might throw an exception are above. If we have established
+        // one of the non-exception error conditions we can stop here.
+        if (result.infinity || result.zero) {
+            return result;
+        }
+
         end = length;
         while (end > 1 && s.charAt(end - 1) == '0') {
             --end;
diff --git a/luni/src/main/java/java/lang/System.java b/luni/src/main/java/java/lang/System.java
index e6303dd..2c5d634 100644
--- a/luni/src/main/java/java/lang/System.java
+++ b/luni/src/main/java/java/lang/System.java
@@ -729,11 +729,6 @@
         }
         p.put("java.home", javaHome);
 
-        String ldLibraryPath = getenv("LD_LIBRARY_PATH");
-        if (ldLibraryPath != null) {
-            p.put("java.library.path", ldLibraryPath);
-        }
-
         p.put("java.specification.name", "Dalvik Core Library");
         p.put("java.specification.vendor", projectName);
         p.put("java.specification.version", "0.9");
diff --git a/luni/src/main/java/java/net/AddressCache.java b/luni/src/main/java/java/net/AddressCache.java
index 194761a..2aba78b 100644
--- a/luni/src/main/java/java/net/AddressCache.java
+++ b/luni/src/main/java/java/net/AddressCache.java
@@ -37,8 +37,36 @@
     private static final long TTL_NANOS = 2 * 1000000000L;
 
     // The actual cache.
-    private final BasicLruCache<String, AddressCacheEntry> cache
-            = new BasicLruCache<String, AddressCacheEntry>(MAX_ENTRIES);
+    private final BasicLruCache<AddressCacheKey, AddressCacheEntry> cache
+            = new BasicLruCache<AddressCacheKey, AddressCacheEntry>(MAX_ENTRIES);
+
+    static class AddressCacheKey {
+        private final String mHostname;
+        private final int mNetId;
+
+        AddressCacheKey(String hostname, int netId) {
+            mHostname = hostname;
+            mNetId = netId;
+        }
+
+        @Override public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof AddressCacheKey)) {
+                return false;
+            }
+            AddressCacheKey lhs = (AddressCacheKey) o;
+            return mHostname.equals(lhs.mHostname) && mNetId == lhs.mNetId;
+        }
+
+        @Override public int hashCode() {
+            int result = 17;
+            result = 31 * result + mNetId;
+            result = 31 * result + mHostname.hashCode();
+            return result;
+        }
+    }
 
     static class AddressCacheEntry {
         // Either an InetAddress[] for a positive entry,
@@ -67,12 +95,12 @@
     }
 
     /**
-     * Returns the cached InetAddress[] associated with 'hostname'. Returns null if nothing is known
-     * about 'hostname'. Returns a String suitable for use as an UnknownHostException detail
-     * message if 'hostname' is known not to exist.
+     * Returns the cached InetAddress[] for 'hostname' on network 'netId'. Returns null
+     * if nothing is known about 'hostname'. Returns a String suitable for use as an
+     * UnknownHostException detail message if 'hostname' is known not to exist.
      */
-    public Object get(String hostname) {
-        AddressCacheEntry entry = cache.get(hostname);
+    public Object get(String hostname, int netId) {
+        AddressCacheEntry entry = cache.get(new AddressCacheKey(hostname, netId));
         // Do we have a valid cache entry?
         if (entry != null && entry.expiryNanos >= System.nanoTime()) {
             return entry.value;
@@ -86,15 +114,15 @@
      * Associates the given 'addresses' with 'hostname'. The association will expire after a
      * certain length of time.
      */
-    public void put(String hostname, InetAddress[] addresses) {
-        cache.put(hostname, new AddressCacheEntry(addresses));
+    public void put(String hostname, int netId, InetAddress[] addresses) {
+        cache.put(new AddressCacheKey(hostname, netId), new AddressCacheEntry(addresses));
     }
 
     /**
      * Records that 'hostname' is known not to have any associated addresses. (I.e. insert a
      * negative cache entry.)
      */
-    public void putUnknownHost(String hostname, String detailMessage) {
-        cache.put(hostname, new AddressCacheEntry(detailMessage));
+    public void putUnknownHost(String hostname, int netId, String detailMessage) {
+        cache.put(new AddressCacheKey(hostname, netId), new AddressCacheEntry(detailMessage));
     }
 }
diff --git a/luni/src/main/java/java/net/InetAddress.java b/luni/src/main/java/java/net/InetAddress.java
index e31b4c3..5cfa15a 100644
--- a/luni/src/main/java/java/net/InetAddress.java
+++ b/luni/src/main/java/java/net/InetAddress.java
@@ -127,6 +127,9 @@
 
     private static final long serialVersionUID = 3286316764910316507L;
 
+    /** Using NetID of NETID_UNSET indicates resolution should be done on default network. */
+    private static final int NETID_UNSET = 0;
+
     private int family;
 
     byte[] ipaddress;
@@ -209,14 +212,29 @@
      * @throws UnknownHostException if the address lookup fails.
      */
     public static InetAddress[] getAllByName(String host) throws UnknownHostException {
-        return getAllByNameImpl(host).clone();
+        return getAllByNameImpl(host, NETID_UNSET).clone();
     }
 
     /**
-     * Returns the InetAddresses for {@code host}. The returned array is shared
-     * and must be cloned before it is returned to application code.
+     * Operates identically to {@code getAllByName} except host resolution is
+     * performed on the network designated by {@code netId}.
+     *
+     * @param host the hostname or literal IP string to be resolved.
+     * @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
      */
-    private static InetAddress[] getAllByNameImpl(String host) throws UnknownHostException {
+    public static InetAddress[] getAllByNameOnNet(String host, int netId) throws UnknownHostException {
+        return getAllByNameImpl(host, netId).clone();
+    }
+
+    /**
+     * Returns the InetAddresses for {@code host} on network {@code netId}. The
+     * returned array is shared and must be cloned before it is returned to
+     * application code.
+     */
+    private static InetAddress[] getAllByNameImpl(String host, int netId) throws UnknownHostException {
         if (host == null || host.isEmpty()) {
             return loopbackAddresses();
         }
@@ -231,7 +249,7 @@
             return new InetAddress[] { result };
         }
 
-        return lookupHostByName(host).clone();
+        return lookupHostByName(host, netId).clone();
     }
 
     private static InetAddress makeInetAddress(byte[] bytes, String hostName) throws UnknownHostException {
@@ -264,7 +282,7 @@
         hints.ai_flags = AI_NUMERICHOST;
         InetAddress[] addresses = null;
         try {
-            addresses = Libcore.os.getaddrinfo(address, hints);
+            addresses = Libcore.os.android_getaddrinfo(address, hints, NETID_UNSET);
         } catch (GaiException ignored) {
         }
         return (addresses != null) ? addresses[0] : null;
@@ -284,7 +302,22 @@
      *             if the address lookup fails.
      */
     public static InetAddress getByName(String host) throws UnknownHostException {
-        return getAllByNameImpl(host)[0];
+        return getAllByNameImpl(host, NETID_UNSET)[0];
+    }
+
+    /**
+     * Operates identically to {@code getByName} except host resolution is
+     * performed on the network designated by {@code netId}.
+     *
+     * @param host
+     *            the hostName to be resolved to an address or {@code null}.
+     * @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
+     */
+    public static InetAddress getByNameOnNet(String host, int netId) throws UnknownHostException {
+        return getAllByNameImpl(host, netId)[0];
     }
 
     /**
@@ -360,7 +393,7 @@
      */
     public static InetAddress getLocalHost() throws UnknownHostException {
         String host = Libcore.os.uname().nodename;
-        return lookupHostByName(host)[0];
+        return lookupHostByName(host, NETID_UNSET)[0];
     }
 
     /**
@@ -377,12 +410,14 @@
      * Resolves a hostname to its IP addresses using a cache.
      *
      * @param host the hostname to resolve.
+     * @param netId the network to perform resolution upon.
      * @return the IP addresses of the host.
      */
-    private static InetAddress[] lookupHostByName(String host) throws UnknownHostException {
+    private static InetAddress[] lookupHostByName(String host, int netId)
+            throws UnknownHostException {
         BlockGuard.getThreadPolicy().onNetwork();
         // Do we have a result cached?
-        Object cachedResult = addressCache.get(host);
+        Object cachedResult = addressCache.get(host, netId);
         if (cachedResult != null) {
             if (cachedResult instanceof InetAddress[]) {
                 // A cached positive result.
@@ -400,12 +435,12 @@
             // for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family
             // anyway, just pick one.
             hints.ai_socktype = SOCK_STREAM;
-            InetAddress[] addresses = Libcore.os.getaddrinfo(host, hints);
+            InetAddress[] addresses = Libcore.os.android_getaddrinfo(host, hints, netId);
             // TODO: should getaddrinfo set the hostname of the InetAddresses it returns?
             for (InetAddress address : addresses) {
                 address.hostName = host;
             }
-            addressCache.put(host, addresses);
+            addressCache.put(host, netId, addresses);
             return addresses;
         } catch (GaiException gaiException) {
             // If the failure appears to have been a lack of INTERNET permission, throw a clear
@@ -418,7 +453,7 @@
             }
             // Otherwise, throw an UnknownHostException.
             String detailMessage = "Unable to resolve host \"" + host + "\": " + Libcore.os.gai_strerror(gaiException.error);
-            addressCache.putUnknownHost(host, detailMessage);
+            addressCache.putUnknownHost(host, netId, detailMessage);
             throw gaiException.rethrowAsUnknownHostException(detailMessage);
         }
     }
diff --git a/luni/src/main/java/java/nio/ByteBuffer.java b/luni/src/main/java/java/nio/ByteBuffer.java
index 5873590..61093fa 100644
--- a/luni/src/main/java/java/nio/ByteBuffer.java
+++ b/luni/src/main/java/java/nio/ByteBuffer.java
@@ -69,7 +69,11 @@
         if (capacity < 0) {
             throw new IllegalArgumentException("capacity < 0: " + capacity);
         }
-        return new DirectByteBuffer(MemoryBlock.allocate(capacity), capacity, 0, false, null);
+        // Ensure alignment by 8.
+        MemoryBlock memoryBlock = MemoryBlock.allocate(capacity + 7);
+        long address = memoryBlock.toLong();
+        long alignedAddress = (address + 7) & ~(long)7;
+        return new DirectByteBuffer(memoryBlock, capacity, (int)(alignedAddress - address), false, null);
     }
 
     /**
diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java
index e736c40..9008637 100644
--- a/luni/src/main/java/java/nio/DatagramChannelImpl.java
+++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java
@@ -130,7 +130,6 @@
         }
     }
 
-    /** @hide Until ready for a public API change */
     @Override
     synchronized public boolean isConnected() {
         return connected;
diff --git a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
index 7185c32..ae33672 100644
--- a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
@@ -55,7 +55,6 @@
         return socket;
     }
 
-    /** @hide Until ready for a public API change */
     @Override
     public SocketChannel accept() throws IOException {
         if (!isOpen()) {
diff --git a/luni/src/main/java/java/security/Security.java b/luni/src/main/java/java/security/Security.java
index 81bafbd..b859f9a 100644
--- a/luni/src/main/java/java/security/Security.java
+++ b/luni/src/main/java/java/security/Security.java
@@ -348,6 +348,7 @@
      * Sets the value of the specified security property.
      */
     public static void setProperty(String key, String value) {
+        Services.setNeedRefresh();
         secprops.put(key, value);
     }
 
diff --git a/luni/src/main/java/java/text/DateFormatSymbols.java b/luni/src/main/java/java/text/DateFormatSymbols.java
index 0d33d75..cb9fdac 100644
--- a/luni/src/main/java/java/text/DateFormatSymbols.java
+++ b/luni/src/main/java/java/text/DateFormatSymbols.java
@@ -102,7 +102,7 @@
      *            the locale.
      */
     public DateFormatSymbols(Locale locale) {
-        this.locale = locale;
+        this.locale = LocaleData.mapInvalidAndNullLocales(locale);
         this.localPatternChars = SimpleDateFormat.PATTERN_CHARS;
 
         this.localeData = LocaleData.get(locale);
@@ -152,7 +152,12 @@
 
     private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
         ois.defaultReadObject();
-        this.localeData = LocaleData.get(locale);
+
+        // NOTE: We don't serialize the locale we were created with, so we can't
+        // get back the localeData object we want. This is broken for callers that
+        // access this field directly (i.e, SimpleDateFormat). We should ideally
+        // have serialized the locale we were created with.
+        this.localeData = LocaleData.get(Locale.getDefault());
     }
 
     private void writeObject(ObjectOutputStream oos) throws IOException {
diff --git a/luni/src/main/java/java/text/DecimalFormatSymbols.java b/luni/src/main/java/java/text/DecimalFormatSymbols.java
index 1611594..6e25c1b 100644
--- a/luni/src/main/java/java/text/DecimalFormatSymbols.java
+++ b/luni/src/main/java/java/text/DecimalFormatSymbols.java
@@ -81,6 +81,7 @@
      *            the locale.
      */
     public DecimalFormatSymbols(Locale locale) {
+        locale = LocaleData.mapInvalidAndNullLocales(locale);
         LocaleData localeData = LocaleData.get(locale);
         this.zeroDigit = localeData.zeroDigit;
         this.digit = '#';
diff --git a/luni/src/main/java/java/util/Collections.java b/luni/src/main/java/java/util/Collections.java
index ce4e579..4541d64 100644
--- a/luni/src/main/java/java/util/Collections.java
+++ b/luni/src/main/java/java/util/Collections.java
@@ -1212,7 +1212,7 @@
                 int length = c.size();
                 Object[] result = new Object[length];
                 Iterator<?> it = iterator();
-                for (int i = length; --i >= 0;) {
+                for (int i = 0; i < length; i++) {
                     result[i] = it.next();
                 }
                 return result;
diff --git a/luni/src/main/java/java/util/GregorianCalendar.java b/luni/src/main/java/java/util/GregorianCalendar.java
index 71b79dd..be96684 100644
--- a/luni/src/main/java/java/util/GregorianCalendar.java
+++ b/luni/src/main/java/java/util/GregorianCalendar.java
@@ -331,7 +331,16 @@
         setTimeInMillis(System.currentTimeMillis());
     }
 
-    GregorianCalendar(boolean ignored) {
+    /**
+     * A minimum-cost constructor that does not initialize the current time or perform any date
+     * calculations. For use internally when the time will be set later. Other constructors, such as
+     * {@link GregorianCalendar#GregorianCalendar()}, set the time to the current system clock
+     * and recalculate the fields incurring unnecessary cost when the time or fields will be set
+     * later.
+     *
+     * @hide used internally
+     */
+    public GregorianCalendar(boolean ignored) {
         super(TimeZone.getDefault());
         setFirstDayOfWeek(SUNDAY);
         setMinimalDaysInFirstWeek(1);
diff --git a/luni/src/main/java/java/util/IllformedLocaleException.java b/luni/src/main/java/java/util/IllformedLocaleException.java
index db1754e..3dec1cd 100644
--- a/luni/src/main/java/java/util/IllformedLocaleException.java
+++ b/luni/src/main/java/java/util/IllformedLocaleException.java
@@ -21,7 +21,6 @@
  *
  * See {@link Locale} and {@link Locale.Builder}.
  *
- * @hide
  * @since 1.7
  */
 public class IllformedLocaleException extends RuntimeException {
diff --git a/luni/src/main/java/java/util/Locale.java b/luni/src/main/java/java/util/Locale.java
index a3eaf21..a8ff15d 100644
--- a/luni/src/main/java/java/util/Locale.java
+++ b/luni/src/main/java/java/util/Locale.java
@@ -253,7 +253,6 @@
      *
      * See {@link #getExtension(char)} and {@link Builder#setExtension(char, String)}.
      *
-     * @hide
      * @since 1.7
      */
     public static final char PRIVATE_USE_EXTENSION = 'x';
@@ -264,12 +263,16 @@
      *
      * See {@link #getExtension(char)} and {@link Builder#setExtension(char, String)}.
      *
-     * @hide
      * @since 1.7
      */
     public static final char UNICODE_LOCALE_EXTENSION = 'u';
 
     /**
+     * ISO 639-3 generic code for undetermined languages.
+     */
+    private static final String UNDETERMINED_LANGUAGE = "und";
+
+    /**
      * The current default locale. It is temporarily assigned to US because we
      * need a default locale to lookup the real default locale.
      */
@@ -297,7 +300,6 @@
      * the structured state (keywords and attributes) specified therein.
      *
      * @since 1.7
-     * @hide
      */
     public static final class Builder {
         private String language;
@@ -340,18 +342,22 @@
          * @throws IllformedLocaleException if the language was invalid.
          */
         public Builder setLanguage(String language) {
-            this.language = normalizeAndValidateLanguage(language);
+            this.language = normalizeAndValidateLanguage(language, true /* strict */);
             return this;
         }
 
-        private static String normalizeAndValidateLanguage(String language) {
+        private static String normalizeAndValidateLanguage(String language, boolean strict) {
             if (language == null || language.isEmpty()) {
                 return "";
             }
 
             final String lowercaseLanguage = language.toLowerCase(Locale.ROOT);
             if (!isValidBcp47Alpha(lowercaseLanguage, 2, 3)) {
-                throw new IllformedLocaleException("Invalid language: " + language);
+                if (strict) {
+                    throw new IllformedLocaleException("Invalid language: " + language);
+                } else {
+                    return UNDETERMINED_LANGUAGE;
+                }
             }
 
             return lowercaseLanguage;
@@ -377,7 +383,7 @@
                 return this;
             }
 
-            final Locale fromIcu = ICU.forLanguageTag(languageTag, true /* strict */);
+            final Locale fromIcu = forLanguageTag(languageTag, true /* strict */);
             // When we ask ICU for strict parsing, it might return a null locale
             // if the language tag is malformed.
             if (fromIcu == null) {
@@ -400,11 +406,11 @@
          * @throws IllformedLocaleException if {@code} region is invalid.
          */
         public Builder setRegion(String region) {
-            this.region = normalizeAndValidateRegion(region);
+            this.region = normalizeAndValidateRegion(region, true /* strict */);
             return this;
         }
 
-        private static String normalizeAndValidateRegion(String region) {
+        private static String normalizeAndValidateRegion(String region, boolean strict) {
             if (region == null || region.isEmpty()) {
                 return "";
             }
@@ -412,7 +418,11 @@
             final String uppercaseRegion = region.toUpperCase(Locale.ROOT);
             if (!isValidBcp47Alpha(uppercaseRegion, 2, 2) &&
                     !isUnM49AreaCode(uppercaseRegion)) {
-                throw new IllformedLocaleException("Invalid region: " + region);
+                if (strict) {
+                    throw new IllformedLocaleException("Invalid region: " + region);
+                } else {
+                    return "";
+                }
             }
 
             return uppercaseRegion;
@@ -453,27 +463,32 @@
             String[] subTags = normalizedVariant.split("_");
 
             for (String subTag : subTags) {
-                // The BCP-47 spec states that :
-                // - Subtags can be between [5, 8] alphanumeric chars in length.
-                // - Subtags that start with a number are allowed to be 4 chars in length.
-                if (subTag.length() >= 5 && subTag.length() <= 8) {
-                    if (!isAsciiAlphaNum(subTag)) {
-                        throw new IllformedLocaleException("Invalid variant: " + variant);
-                    }
-                } else if (subTag.length() == 4) {
-                    final char firstChar = subTag.charAt(0);
-                    if (!(firstChar >= '0' && firstChar <= '9') || !isAsciiAlphaNum(subTag)) {
-                        throw new IllformedLocaleException("Invalid variant: " + variant);
-                    }
-                } else {
+                if (!isValidVariantSubtag(subTag)) {
                     throw new IllformedLocaleException("Invalid variant: " + variant);
                 }
             }
 
-
             return normalizedVariant;
         }
 
+        private static boolean isValidVariantSubtag(String subTag) {
+            // The BCP-47 spec states that :
+            // - Subtags can be between [5, 8] alphanumeric chars in length.
+            // - Subtags that start with a number are allowed to be 4 chars in length.
+            if (subTag.length() >= 5 && subTag.length() <= 8) {
+                if (isAsciiAlphaNum(subTag)) {
+                    return true;
+                }
+            } else if (subTag.length() == 4) {
+                final char firstChar = subTag.charAt(0);
+                if ((firstChar >= '0' && firstChar <= '9') && isAsciiAlphaNum(subTag)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
         /**
          * Sets the locale script. If {@code script} is {@code null} or empty,
          * the previous value is cleared.
@@ -489,17 +504,24 @@
          * @throws IllformedLocaleException if {@code script} is invalid.
          */
         public Builder setScript(String script) {
+            this.script = normalizeAndValidateScript(script, true /* strict */);
+            return this;
+        }
+
+        private static String normalizeAndValidateScript(String script, boolean strict) {
             if (script == null || script.isEmpty()) {
-                this.script = "";
-                return this;
+                return "";
             }
 
             if (!isValidBcp47Alpha(script, 4, 4)) {
-                throw new IllformedLocaleException("Invalid script: " + script);
+                if (strict) {
+                    throw new IllformedLocaleException("Invalid script: " + script);
+                } else {
+                    return "";
+                }
             }
 
-            this.script = titleCaseAsciiWord(script);
-            return this;
+            return titleCaseAsciiWord(script);
         }
 
         /**
@@ -787,7 +809,6 @@
      *
      * @throws NullPointerException if {@code languageTag} is {@code null}.
      *
-     * @hide
      * @since 1.7
      */
     public static Locale forLanguageTag(String languageTag) {
@@ -795,7 +816,7 @@
             throw new NullPointerException("languageTag == null");
         }
 
-        return ICU.forLanguageTag(languageTag, false /* strict */);
+        return forLanguageTag(languageTag, false /* strict */);
     }
 
     private transient String countryCode;
@@ -1005,9 +1026,9 @@
             return "";
         }
 
-        try {
-            Builder.normalizeAndValidateRegion(countryCode);
-        } catch (IllformedLocaleException ex) {
+        final String normalizedRegion = Builder.normalizeAndValidateRegion(
+                countryCode, false /* strict */);
+        if (normalizedRegion.isEmpty()) {
             return countryCode;
         }
 
@@ -1041,9 +1062,9 @@
         // display language for the indeterminate language code.
         //
         // Sigh... ugh... and what not.
-        try {
-            Builder.normalizeAndValidateLanguage(languageCode);
-        } catch (IllformedLocaleException ex) {
+        final String normalizedLanguage = Builder.normalizeAndValidateLanguage(
+                languageCode, false /* strict */);
+        if (UNDETERMINED_LANGUAGE.equals(normalizedLanguage)) {
             return languageCode;
         }
 
@@ -1125,6 +1146,8 @@
      * Returns the full variant name in the default {@code Locale} for the variant code of
      * this {@code Locale}. If there is no matching variant name, the variant code is
      * returned.
+     *
+     * @since 1.7
      */
     public final String getDisplayVariant() {
         return getDisplayVariant(getDefault());
@@ -1134,6 +1157,8 @@
      * Returns the full variant name in the specified {@code Locale} for the variant code
      * of this {@code Locale}. If there is no matching variant name, the variant code is
      * returned.
+     *
+     * @since 1.7
      */
     public String getDisplayVariant(Locale locale) {
         if (variantCode.isEmpty()) {
@@ -1238,7 +1263,6 @@
      * If set, the script code will be a title cased string of length 4, as per the ISO 15924
      * specification.
      *
-     * @hide
      * @since 1.7
      */
     public String getScript() {
@@ -1248,7 +1272,6 @@
     /**
      * Equivalent to {@code getDisplayScript(Locale.getDefault()))}
      *
-     * @hide
      * @since 1.7
      */
     public String getDisplayScript() {
@@ -1260,7 +1283,6 @@
      * script code is unknown, the return value of this method is the same as that of
      * {@link #getScript()}.
      *
-     * @hide
      * @since 1.7
      */
     public String getDisplayScript(Locale locale) {
@@ -1296,7 +1318,6 @@
      * For example, we do not require scripts to be a registered ISO 15924 scripts or
      * languages to appear in the ISO-639-2 code list.
      *
-     * @hide
      * @since 1.7
      */
     public String toLanguageTag() {
@@ -1331,17 +1352,8 @@
             // in the builder, but we must use hyphens in the BCP-47 language tag.
             variant = variantCode.replace('_', '-');
         } else {
-            try {
-                language = Builder.normalizeAndValidateLanguage(languageCode);
-            } catch (IllformedLocaleException ilfe) {
-                // Ignored, continue processing with "".
-            }
-
-            try {
-                region = Builder.normalizeAndValidateRegion(countryCode);
-            } catch (IllformedLocaleException ilfe) {
-                // Ignored, continue processing with "".
-            }
+            language = Builder.normalizeAndValidateLanguage(languageCode, false /* strict */);
+            region = Builder.normalizeAndValidateRegion(countryCode, false /* strict */);
 
             try {
                 variant = Builder.normalizeAndValidateVariant(variantCode);
@@ -1358,7 +1370,7 @@
         }
 
         if (language.isEmpty()) {
-            language = "und";
+            language = UNDETERMINED_LANGUAGE;
         }
 
         if ("no".equals(language) && "NO".equals(region) && "NY".equals(variant)) {
@@ -1497,7 +1509,7 @@
     private static String concatenateRange(String[] array, int start, int end) {
         StringBuilder builder = new StringBuilder(32);
         for (int i = start; i < end; ++i) {
-            if (i != 0) {
+            if (i != start) {
                 builder.append('-');
             }
             builder.append(array[i]);
@@ -1512,7 +1524,6 @@
      * See <a href="https://tools.ietf.org/html/bcp47#section-2.1">
      *     the IETF BCP-47 specification</a> (Section 2.2.6) for details.
      *
-     * @hide
      * @since 1.7
      */
     public Set<Character> getExtensionKeys() {
@@ -1527,7 +1538,6 @@
      * locale extension can be fetched using {@link #getUnicodeLocaleAttributes()},
      * {@link #getUnicodeLocaleKeys()}  and {@link #getUnicodeLocaleType}.
      *
-     * @hide
      * @since 1.7
      */
     public String getExtension(char extensionKey) {
@@ -1540,7 +1550,6 @@
      * For more information about types and keywords, see {@link Builder#setUnicodeLocaleKeyword}
      * and <a href="http://www.unicode.org/reports/tr35/#BCP47">Unicode Technical Standard #35</a>
      *
-     * @hide
      * @since 1.7
      */
     public String getUnicodeLocaleType(String keyWord) {
@@ -1553,7 +1562,6 @@
      * For more information about attributes, see {@link Builder#addUnicodeLocaleAttribute}
      * and <a href="http://www.unicode.org/reports/tr35/#BCP47">Unicode Technical Standard #35</a>
      *
-     * @hide
      * @since 1.7
      */
     public Set<String> getUnicodeLocaleAttributes() {
@@ -1566,7 +1574,6 @@
      * For more information about types and keywords, see {@link Builder#setUnicodeLocaleKeyword}
      * and <a href="http://www.unicode.org/reports/tr35/#BCP47">Unicode Technical Standard #35</a>
      *
-     * @hide
      * @since 1.7
      */
     public Set<String> getUnicodeLocaleKeys() {
@@ -1922,8 +1929,10 @@
             while (true) {
                 final Map.Entry<String, String> keyWord = keywordsIterator.next();
                 sb.append(keyWord.getKey());
-                sb.append('-');
-                sb.append(keyWord.getValue());
+                if (!keyWord.getValue().isEmpty()) {
+                    sb.append('-');
+                    sb.append(keyWord.getValue());
+                }
                 if (keywordsIterator.hasNext()) {
                     sb.append('-');
                 } else {
@@ -1970,6 +1979,8 @@
 
         if (subtagsForKeyword.size() > 0) {
             keywords.put(lastKeyword, joinBcp47Subtags(subtagsForKeyword));
+        } else {
+            keywords.put(lastKeyword, "");
         }
     }
 
@@ -1991,7 +2002,10 @@
         return sb.toString();
     }
 
-    private static String adjustLanguageCode(String languageCode) {
+    /**
+     * @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.
@@ -2005,4 +2019,230 @@
 
         return adjusted;
     }
+
+    /**
+     * Map of grandfathered language tags to their modern replacements.
+     */
+    private static final TreeMap<String, String> GRANDFATHERED_LOCALES;
+
+    static {
+        GRANDFATHERED_LOCALES = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
+
+        // From http://tools.ietf.org/html/bcp47
+        //
+        // grandfathered = irregular           ; non-redundant tags registered
+        //               / regular             ; during the RFC 3066 era
+        //  irregular =
+        GRANDFATHERED_LOCALES.put("en-GB-oed", "en-GB-x-oed");
+        GRANDFATHERED_LOCALES.put("i-ami", "ami");
+        GRANDFATHERED_LOCALES.put("i-bnn", "bnn");
+        GRANDFATHERED_LOCALES.put("i-default", "en-x-i-default");
+        GRANDFATHERED_LOCALES.put("i-enochian", "und-x-i-enochian");
+        GRANDFATHERED_LOCALES.put("i-hak", "hak");
+        GRANDFATHERED_LOCALES.put("i-klingon", "tlh");
+        GRANDFATHERED_LOCALES.put("i-lux", "lb");
+        GRANDFATHERED_LOCALES.put("i-mingo", "see-x-i-mingo");
+        GRANDFATHERED_LOCALES.put("i-navajo", "nv");
+        GRANDFATHERED_LOCALES.put("i-pwn", "pwn");
+        GRANDFATHERED_LOCALES.put("i-tao", "tao");
+        GRANDFATHERED_LOCALES.put("i-tay", "tay");
+        GRANDFATHERED_LOCALES.put("i-tsu", "tsu");
+        GRANDFATHERED_LOCALES.put("sgn-BE-FR", "sfb");
+        GRANDFATHERED_LOCALES.put("sgn-BE-NL", "vgt");
+        GRANDFATHERED_LOCALES.put("sgn-CH-DE", "sgg");
+
+        // regular =
+        GRANDFATHERED_LOCALES.put("art-lojban", "jbo");
+        GRANDFATHERED_LOCALES.put("cel-gaulish", "xtg-x-cel-gaulish");
+        GRANDFATHERED_LOCALES.put("no-bok", "nb");
+        GRANDFATHERED_LOCALES.put("no-nyn", "nn");
+        GRANDFATHERED_LOCALES.put("zh-guoyu", "cmn");
+        GRANDFATHERED_LOCALES.put("zh-hakka", "hak");
+        GRANDFATHERED_LOCALES.put("zh-min", "nan-x-zh-min");
+        GRANDFATHERED_LOCALES.put("zh-min-nan", "nan");
+        GRANDFATHERED_LOCALES.put("zh-xiang", "hsn");
+    }
+
+    private static String convertGrandfatheredTag(String original) {
+        final String converted = GRANDFATHERED_LOCALES.get(original);
+        return converted != null ? converted : original;
+    }
+
+    /**
+     * Scans elements of {@code subtags} in the range {@code [startIndex, endIndex)}
+     * and appends valid variant subtags upto the first invalid subtag  (if any) to
+     * {@code normalizedVariants}. All appended variant subtags are converted to uppercase.
+     */
+    private static void extractVariantSubtags(String[] subtags, int startIndex, int endIndex,
+            List<String> normalizedVariants) {
+        for (int i = startIndex; i < endIndex; i++) {
+            final String subtag = subtags[i];
+
+            if (Builder.isValidVariantSubtag(subtag)) {
+                normalizedVariants.add(subtag.toUpperCase(Locale.ROOT));
+            } else {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Scans elements of {@code subtags} in the range {@code [startIndex, endIndex)}
+     * and inserts valid extensions into {@code extensions}. The scan is aborted
+     * when an invalid extension is encountered. Returns the index of the first
+     * unparsable element of {@code subtags}.
+     */
+    private static int extractExtensions(String[] subtags, int startIndex, int endIndex,
+            Map<Character, String> extensions) {
+        int privateUseExtensionIndex = -1;
+        int extensionKeyIndex = -1;
+
+        int i = startIndex;
+        for (; i < endIndex; i++) {
+            final String subtag = subtags[i];
+
+            final boolean parsingPrivateUse = (privateUseExtensionIndex != -1) &&
+                    (extensionKeyIndex == privateUseExtensionIndex);
+
+            // Note that private use extensions allow subtags of length 1.
+            // Private use extensions *must* come last, so there's no ambiguity
+            // in that case.
+            if (subtag.length() == 1 && !parsingPrivateUse) {
+                // Emit the last extension we encountered if any. First check
+                // whether we encountered two keys in a row (which is an error).
+                // Also checks if we already have an extension with the same key,
+                // which is again an error.
+                if (extensionKeyIndex != -1) {
+                    if ((i - 1) == extensionKeyIndex) {
+                        return extensionKeyIndex;
+                    }
+
+                    final String key = subtags[extensionKeyIndex];
+                    if (extensions.containsKey(key.charAt(0))) {
+                        return extensionKeyIndex;
+                    }
+
+                    final String value = concatenateRange(subtags, extensionKeyIndex + 1, i);
+                    extensions.put(key.charAt(0), value.toLowerCase(Locale.ROOT));
+                }
+
+                // Mark the start of the next extension. Also keep track of whether this
+                // is a private use extension, and throw an error if it doesn't come last.
+                extensionKeyIndex = i;
+                if ("x".equals(subtag)) {
+                    privateUseExtensionIndex = i;
+                } else if (privateUseExtensionIndex != -1) {
+                    // The private use extension must come last.
+                    return privateUseExtensionIndex;
+                }
+            } else if (extensionKeyIndex != -1) {
+                // We must have encountered a valid key in order to start parsing
+                // its subtags.
+                if (!isValidBcp47Alphanum(subtag, parsingPrivateUse ? 1 : 2, 8)) {
+                    return i;
+                }
+            } else {
+                // Encountered a value without a preceding key.
+                return i;
+            }
+        }
+
+        if (extensionKeyIndex != -1) {
+            if ((i - 1) == extensionKeyIndex) {
+                return extensionKeyIndex;
+            }
+
+            final String key = subtags[extensionKeyIndex];
+            if (extensions.containsKey(key.charAt(0))) {
+                return extensionKeyIndex;
+            }
+
+            final String value = concatenateRange(subtags, extensionKeyIndex + 1, i);
+            extensions.put(key.charAt(0), value.toLowerCase(Locale.ROOT));
+        }
+
+        return i;
+    }
+
+    private static Locale forLanguageTag(/* @Nonnull */ String tag, boolean strict) {
+        final String converted = convertGrandfatheredTag(tag);
+        final String[] subtags = converted.split("-");
+
+        int lastSubtag = subtags.length;
+        for (int i = 0; i < subtags.length; ++i) {
+            final String subtag = subtags[i];
+            if (subtag.isEmpty() || subtag.length() > 8) {
+                if (strict) {
+                    throw new IllformedLocaleException("Invalid subtag at index: " + i
+                            + " in tag: " + tag);
+                } else {
+                    lastSubtag = (i - 1);
+                }
+
+                break;
+            }
+        }
+
+        final String languageCode = Builder.normalizeAndValidateLanguage(subtags[0], strict);
+        String scriptCode = "";
+        int nextSubtag = 1;
+        if (lastSubtag > nextSubtag) {
+            scriptCode = Builder.normalizeAndValidateScript(subtags[nextSubtag], false /* strict */);
+            if (!scriptCode.isEmpty()) {
+                nextSubtag++;
+            }
+        }
+
+        String regionCode = "";
+        if (lastSubtag > nextSubtag) {
+            regionCode = Builder.normalizeAndValidateRegion(subtags[nextSubtag], false /* strict */);
+            if (!regionCode.isEmpty()) {
+                nextSubtag++;
+            }
+        }
+
+        List<String> variants = null;
+        if (lastSubtag > nextSubtag) {
+            variants = new ArrayList<String>();
+            extractVariantSubtags(subtags, nextSubtag, lastSubtag, variants);
+            nextSubtag += variants.size();
+        }
+
+        Map<Character, String> extensions = Collections.EMPTY_MAP;
+        if (lastSubtag > nextSubtag) {
+            extensions = new TreeMap<Character, String>();
+            nextSubtag = extractExtensions(subtags, nextSubtag, lastSubtag, extensions);
+        }
+
+        if (nextSubtag != lastSubtag) {
+            if (strict) {
+                throw new IllformedLocaleException("Unparseable subtag: " + subtags[nextSubtag]
+                        + " from language tag: " + tag);
+            }
+        }
+
+        Set<String> unicodeKeywords = Collections.EMPTY_SET;
+        Map<String, String> unicodeAttributes = Collections.EMPTY_MAP;
+        if (extensions.containsKey(UNICODE_LOCALE_EXTENSION)) {
+            unicodeKeywords = new TreeSet<String>();
+            unicodeAttributes = new TreeMap<String, String>();
+            parseUnicodeExtension(extensions.get(UNICODE_LOCALE_EXTENSION).split("-"),
+                    unicodeAttributes, unicodeKeywords);
+        }
+
+        String variantCode = "";
+        if (variants != null && !variants.isEmpty()) {
+            StringBuilder variantsBuilder = new StringBuilder(variants.size() * 8);
+            for (int i = 0; i < variants.size(); ++i) {
+                if (i != 0) {
+                    variantsBuilder.append('_');
+                }
+                variantsBuilder.append(variants.get(i));
+            }
+            variantCode = variantsBuilder.toString();
+        }
+
+        return new Locale(languageCode, regionCode, variantCode, scriptCode,
+                unicodeKeywords, unicodeAttributes, extensions, true /* has validated fields */);
+    }
 }
diff --git a/luni/src/main/java/java/util/TimeZone.java b/luni/src/main/java/java/util/TimeZone.java
index c024e8d..e4c68c5 100644
--- a/luni/src/main/java/java/util/TimeZone.java
+++ b/luni/src/main/java/java/util/TimeZone.java
@@ -329,7 +329,6 @@
         }
 
         // Special cases? These can clone an existing instance.
-        // TODO: should we just add a cache to ZoneInfoDB instead?
         if (id.length() == 3) {
             if (id.equals("GMT")) {
                 return (TimeZone) GMT.clone();
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java
index 54b53ae..b38d6a5 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java
@@ -56,8 +56,6 @@
  * actions subsequent to the access or removal of that element from
  * the {@code ConcurrentLinkedDeque} in another thread.
  *
- * @hide
- *
  * @since 1.7
  * @author Doug Lea
  * @author Martin Buchholz
diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinPool.java b/luni/src/main/java/java/util/concurrent/ForkJoinPool.java
index 5ac01c8..9448616 100644
--- a/luni/src/main/java/java/util/concurrent/ForkJoinPool.java
+++ b/luni/src/main/java/java/util/concurrent/ForkJoinPool.java
@@ -39,7 +39,7 @@
  * ForkJoinPool}s may also be appropriate for use with event-style
  * tasks that are never joined.
  *
- * <p>A static {@link #commonPool()} is available and appropriate for
+ * <p>A static {@code commonPool()} is available and appropriate for
  * most applications. The common pool is used by any ForkJoinTask that
  * is not explicitly submitted to a specified pool. Using the common
  * pool normally reduces resource usage (its threads are slowly
@@ -127,7 +127,6 @@
  * or internal resources have been exhausted.
  *
  * @since 1.7
- * @hide
  * @author Doug Lea
  */
 public class ForkJoinPool extends AbstractExecutorService {
@@ -213,8 +212,7 @@
      * choosing existing queues, and may be randomly repositioned upon
      * contention with other submitters.  In essence, submitters act
      * like workers except that they are restricted to executing local
-     * tasks that they submitted (or in the case of CountedCompleters,
-     * others with the same root task).  However, because most
+     * tasks that they submitted. However, because most
      * shared/external queue operations are more expensive than
      * internal, and because, at steady state, external submitters
      * will compete for CPU with workers, ForkJoinTask.join and
@@ -419,12 +417,6 @@
      * to find work (see MAX_HELP) and fall back to suspending the
      * worker and if necessary replacing it with another.
      *
-     * Helping actions for CountedCompleters are much simpler: Method
-     * helpComplete can take and execute any task with the same root
-     * as the task being waited on. However, this still entails some
-     * traversal of completer chains, so is less efficient than using
-     * CountedCompleters without explicit joins.
-     *
      * It is impossible to keep exactly the target parallelism number
      * of threads running at any given time.  Determining the
      * existence of conservatively safe helping targets, the
@@ -2907,7 +2899,7 @@
      * Possibly initiates an orderly shutdown in which previously
      * submitted tasks are executed, but no new tasks will be
      * accepted. Invocation has no effect on execution state if this
-     * is the {@link #commonPool()}, and no additional effect if
+     * is the {@code commonPool()}, and no additional effect if
      * already shut down.  Tasks that are in the process of being
      * submitted concurrently during the course of this method may or
      * may not be rejected.
@@ -2920,7 +2912,7 @@
     /**
      * Possibly attempts to cancel and/or stop all tasks, and reject
      * all subsequently submitted tasks.  Invocation has no effect on
-     * execution state if this is the {@link #commonPool()}, and no
+     * execution state if this is the {@code commonPool()}, and no
      * additional effect if already shut down. Otherwise, tasks that
      * are in the process of being submitted or executed concurrently
      * during the course of this method may or may not be
@@ -2979,8 +2971,8 @@
     /**
      * Blocks until all tasks have completed execution after a
      * shutdown request, or the timeout occurs, or the current thread
-     * is interrupted, whichever happens first. Because the {@link
-     * #commonPool()} never terminates until program shutdown, when
+     * is interrupted, whichever happens first. Because the {@code
+     * commonPool()} never terminates until program shutdown, when
      * applied to the common pool, this method is equivalent to {@link
      * #awaitQuiescence(long, TimeUnit)} but always returns {@code false}.
      *
@@ -3064,7 +3056,7 @@
 
     /**
      * Waits and/or attempts to assist performing tasks indefinitely
-     * until the {@link #commonPool()} {@link #isQuiescent}.
+     * until the {@code commonPool()} {@link #isQuiescent}.
      */
     static void quiesceCommonPool() {
         common.awaitQuiescence(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinTask.java b/luni/src/main/java/java/util/concurrent/ForkJoinTask.java
index 6d25775..c6bc6de 100644
--- a/luni/src/main/java/java/util/concurrent/ForkJoinTask.java
+++ b/luni/src/main/java/java/util/concurrent/ForkJoinTask.java
@@ -32,8 +32,8 @@
  *
  * <p>A "main" {@code ForkJoinTask} begins execution when it is
  * explicitly submitted to a {@link ForkJoinPool}, or, if not already
- * engaged in a ForkJoin computation, commenced in the {@link
- * ForkJoinPool#commonPool()} via {@link #fork}, {@link #invoke}, or
+ * engaged in a ForkJoin computation, commenced in the {@code
+ * ForkJoinPool.commonPool()} via {@link #fork}, {@link #invoke}, or
  * related methods.  Once started, it will usually in turn start other
  * subtasks.  As indicated by the name of this class, many programs
  * using {@code ForkJoinTask} employ only methods {@link #fork} and
@@ -74,10 +74,9 @@
  * but doing do requires three further considerations: (1) Completion
  * of few if any <em>other</em> tasks should be dependent on a task
  * that blocks on external synchronization or I/O. Event-style async
- * tasks that are never joined (for example, those subclassing {@link
- * CountedCompleter}) often fall into this category.  (2) To minimize
- * resource impact, tasks should be small; ideally performing only the
- * (possibly) blocking action. (3) Unless the {@link
+ * tasks that are never joined often fall into this category.
+ * (2) To minimize resource impact, tasks should be small; ideally
+ * performing only the (possibly) blocking action. (3) Unless the {@link
  * ForkJoinPool.ManagedBlocker} API is used, or the number of possibly
  * blocked tasks is known to be less than the pool's {@link
  * ForkJoinPool#getParallelism} level, the pool cannot guarantee that
@@ -120,13 +119,11 @@
  * <p>The ForkJoinTask class is not usually directly subclassed.
  * Instead, you subclass one of the abstract classes that support a
  * particular style of fork/join processing, typically {@link
- * RecursiveAction} for most computations that do not return results,
- * {@link RecursiveTask} for those that do, and {@link
- * CountedCompleter} for those in which completed actions trigger
- * other actions.  Normally, a concrete ForkJoinTask subclass declares
- * fields comprising its parameters, established in a constructor, and
- * then defines a {@code compute} method that somehow uses the control
- * methods supplied by this base class.
+ * RecursiveAction} for most computations that do not return results
+ * and {@link RecursiveTask} for those that do. Normally, a concrete
+ * ForkJoinTask subclass declares fields comprising its parameters,
+ * established in a constructor, and then defines a {@code compute}
+ * method that somehow uses the control methods supplied by this base class.
  *
  * <p>Method {@link #join} and its variants are appropriate for use
  * only when completion dependencies are acyclic; that is, the
@@ -138,9 +135,9 @@
  * may be of use in constructing custom subclasses for problems that
  * are not statically structured as DAGs. To support such usages, a
  * ForkJoinTask may be atomically <em>tagged</em> with a {@code short}
- * value using {@link #setForkJoinTaskTag} or {@link
- * #compareAndSetForkJoinTaskTag} and checked using {@link
- * #getForkJoinTaskTag}. The ForkJoinTask implementation does not use
+ * value using {@code setForkJoinTaskTag} or {@code
+ * compareAndSetForkJoinTaskTag} and checked using {@code
+ * getForkJoinTaskTag}. The ForkJoinTask implementation does not use
  * these {@code protected} methods or tags for any purpose, but they
  * may be of use in the construction of specialized subclasses.  For
  * example, parallel graph traversals can use the supplied methods to
@@ -178,7 +175,6 @@
  * execution. Serialization is not relied on during execution itself.
  *
  * @since 1.7
- * @hide
  * @author Doug Lea
  */
 public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
@@ -645,8 +641,8 @@
 
     /**
      * Arranges to asynchronously execute this task in the pool the
-     * current task is running in, if applicable, or using the {@link
-     * ForkJoinPool#commonPool()} if not {@link #inForkJoinPool}.  While
+     * current task is running in, if applicable, or using the {@code
+     * ForkJoinPool.commonPool()} if not {@link #inForkJoinPool}.  While
      * it is not necessarily enforced, it is a usage error to fork a
      * task more than once unless it has completed and been
      * reinitialized.  Subsequent modifications to the state of this
diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java b/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
index 5f2799b..ae28700 100644
--- a/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
+++ b/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
@@ -18,7 +18,6 @@
  * {@linkplain ForkJoinPool#ForkJoinPool use it} in a {@code ForkJoinPool}.
  *
  * @since 1.7
- * @hide
  * @author Doug Lea
  */
 public class ForkJoinWorkerThread extends Thread {
diff --git a/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java b/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java
index cff5dbf..a041fb1 100644
--- a/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java
+++ b/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java
@@ -50,7 +50,6 @@
  * the {@code LinkedTransferQueue} in another thread.
  *
  * @since 1.7
- * @hide
  * @author Doug Lea
  * @param <E> the type of elements held in this collection
  */
diff --git a/luni/src/main/java/java/util/concurrent/Phaser.java b/luni/src/main/java/java/util/concurrent/Phaser.java
index a9adbe5..a97d187 100644
--- a/luni/src/main/java/java/util/concurrent/Phaser.java
+++ b/luni/src/main/java/java/util/concurrent/Phaser.java
@@ -227,7 +227,6 @@
  * of participants.
  *
  * @since 1.7
- * @hide
  * @author Doug Lea
  */
 public class Phaser {
diff --git a/luni/src/main/java/java/util/concurrent/RecursiveAction.java b/luni/src/main/java/java/util/concurrent/RecursiveAction.java
index 8d666f6..e3a6340 100644
--- a/luni/src/main/java/java/util/concurrent/RecursiveAction.java
+++ b/luni/src/main/java/java/util/concurrent/RecursiveAction.java
@@ -131,7 +131,6 @@
  * }}</pre>
  *
  * @since 1.7
- * @hide
  * @author Doug Lea
  */
 public abstract class RecursiveAction extends ForkJoinTask<Void> {
diff --git a/luni/src/main/java/java/util/concurrent/RecursiveTask.java b/luni/src/main/java/java/util/concurrent/RecursiveTask.java
index 421c9d3..80baa52 100644
--- a/luni/src/main/java/java/util/concurrent/RecursiveTask.java
+++ b/luni/src/main/java/java/util/concurrent/RecursiveTask.java
@@ -34,7 +34,6 @@
  * sequentially solve rather than subdividing.
  *
  * @since 1.7
- * @hide
  * @author Doug Lea
  */
 public abstract class RecursiveTask<V> extends ForkJoinTask<V> {
diff --git a/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java b/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
index a52351b..483981d 100644
--- a/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
+++ b/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
@@ -690,7 +690,6 @@
      * @param value if {@code true}, remove on cancellation, else don't
      * @see #getRemoveOnCancelPolicy
      * @since 1.7
-     * @hide
      */
     public void setRemoveOnCancelPolicy(boolean value) {
         removeOnCancel = value;
@@ -705,7 +704,6 @@
      *         from the queue
      * @see #setRemoveOnCancelPolicy
      * @since 1.7
-     * @hide
      */
     public boolean getRemoveOnCancelPolicy() {
         return removeOnCancel;
diff --git a/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java b/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java
index a559321..5baf75f 100644
--- a/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java
+++ b/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java
@@ -30,7 +30,6 @@
  * generation methods.
  *
  * @since 1.7
- * @hide
  * @author Doug Lea
  */
 public class ThreadLocalRandom extends Random {
diff --git a/luni/src/main/java/java/util/concurrent/TransferQueue.java b/luni/src/main/java/java/util/concurrent/TransferQueue.java
index 9cd5773..4c2be6f 100644
--- a/luni/src/main/java/java/util/concurrent/TransferQueue.java
+++ b/luni/src/main/java/java/util/concurrent/TransferQueue.java
@@ -33,7 +33,6 @@
  * and {@code transfer} are effectively synonymous.
  *
  * @since 1.7
- * @hide
  * @author Doug Lea
  * @param <E> the type of elements held in this collection
  */
diff --git a/luni/src/main/java/java/util/concurrent/atomic/Fences.java b/luni/src/main/java/java/util/concurrent/atomic/Fences.java
index 7ecf45a..5714ba0 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/Fences.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/Fences.java
@@ -453,7 +453,6 @@
  *
  * </dl>
  *
- * @since 1.7
  * @hide
  * @author Doug Lea
  */
diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
index 4c5e280..37aa9d0 100644
--- a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
+++ b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
@@ -1255,7 +1255,6 @@
      *         current thread, and {@code false} if the current thread
      *         is at the head of the queue or the queue is empty
      * @since 1.7
-     * @hide
      */
     public final boolean hasQueuedPredecessors() {
         // The correctness of this depends on head being initialized
diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
index 0350060..e711da5 100644
--- a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
+++ b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
@@ -1485,7 +1485,6 @@
      *         current thread, and {@code false} if the current thread
      *         is at the head of the queue or the queue is empty
      * @since 1.7
-     * @hide
      */
     public final boolean hasQueuedPredecessors() {
         // The correctness of this depends on head being initialized
diff --git a/luni/src/main/java/java/util/jar/JarEntry.java b/luni/src/main/java/java/util/jar/JarEntry.java
index 85c8678..bceef63 100644
--- a/luni/src/main/java/java/util/jar/JarEntry.java
+++ b/luni/src/main/java/java/util/jar/JarEntry.java
@@ -17,16 +17,16 @@
 
 package java.util.jar;
 
-import javax.security.auth.x500.X500Principal;
 import java.io.IOException;
 import java.security.CodeSigner;
 import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
-import java.util.List;
+import java.util.Arrays;
 import java.util.zip.ZipEntry;
 
 /**
@@ -114,8 +114,12 @@
      * entry or {@code null} if none exists. Make sure that the everything is
      * read from the input stream before calling this method, or else the method
      * returns {@code null}.
+     * <p>
+     * This method returns all the signers' unverified chains concatenated
+     * together in one array. To know which certificates were tied to the
+     * private keys that made the signatures on this entry, see
+     * {@link #getCodeSigners()} instead.
      *
-     * @return the certificate for this entry.
      * @see java.security.cert.Certificate
      */
     public Certificate[] getCertificates() {
@@ -126,7 +130,27 @@
         if (jarVerifier == null) {
             return null;
         }
-        return jarVerifier.getCertificates(getName());
+
+        Certificate[][] certChains = jarVerifier.getCertificateChains(getName());
+        if (certChains == null) {
+            return null;
+        }
+
+        // Measure number of certs.
+        int count = 0;
+        for (Certificate[] chain : certChains) {
+            count += chain.length;
+        }
+
+        // Create new array and copy all the certs into it.
+        Certificate[] certs = new Certificate[count];
+        int i = 0;
+        for (Certificate[] chain : certChains) {
+            System.arraycopy(chain, 0, certs, i, chain.length);
+            i += chain.length;
+        }
+
+        return certs;
     }
 
     void setAttributes(Attributes attrib) {
@@ -138,68 +162,60 @@
      * JAR file. If there is no such code signer, it returns {@code null}. Make
      * sure that the everything is read from the input stream before calling
      * this method, or else the method returns {@code null}.
+     * <p>
+     * Only the digital signature on the entry is cryptographically verified.
+     * None of the certificates in the the {@link CertPath} returned from
+     * {@link CodeSigner#getSignerCertPath()} are verified and must be verified
+     * by the caller if needed. See {@link CertPathValidator} for more
+     * information.
      *
-     * @return the code signers for the JAR entry.
+     * @return an array of CodeSigner for this JAR entry.
      * @see CodeSigner
      */
     public CodeSigner[] getCodeSigners() {
+        if (parentJar == null) {
+            return null;
+        }
+
+        JarVerifier jarVerifier = parentJar.verifier;
+        if (jarVerifier == null) {
+            return null;
+        }
+
         if (signers == null) {
-            signers = getCodeSigners(getCertificates());
+            signers = getCodeSigners(jarVerifier.getCertificateChains(getName()));
         }
         if (signers == null) {
             return null;
         }
 
-        CodeSigner[] tmp = new CodeSigner[signers.length];
-        System.arraycopy(signers, 0, tmp, 0, tmp.length);
-        return tmp;
+        return signers.clone();
     }
 
-    private CodeSigner[] getCodeSigners(Certificate[] certs) {
-        if (certs == null) {
+    private CodeSigner[] getCodeSigners(Certificate[][] certChains) {
+        if (certChains == null) {
             return null;
         }
 
-        X500Principal prevIssuer = null;
-        ArrayList<Certificate> list = new ArrayList<Certificate>(certs.length);
-        ArrayList<CodeSigner> asigners = new ArrayList<CodeSigner>();
+        ArrayList<CodeSigner> asigners = new ArrayList<CodeSigner>(certChains.length);
 
-        for (Certificate element : certs) {
-            if (!(element instanceof X509Certificate)) {
-                // Only X509Certificate-s are taken into account - see API spec.
-                continue;
-            }
-            X509Certificate x509 = (X509Certificate) element;
-            if (prevIssuer != null) {
-                X500Principal subj = x509.getSubjectX500Principal();
-                if (!prevIssuer.equals(subj)) {
-                    // Ok, this ends the previous chain,
-                    // so transform this one into CertPath ...
-                    addCodeSigner(asigners, list);
-                    // ... and start a new one
-                    list.clear();
-                }// else { it's still the same chain }
-
-            }
-            prevIssuer = x509.getIssuerX500Principal();
-            list.add(x509);
-        }
-        if (!list.isEmpty()) {
-            addCodeSigner(asigners, list);
-        }
-        if (asigners.isEmpty()) {
-            // 'signers' is 'null' already
-            return null;
+        for (Certificate[] chain : certChains) {
+            addCodeSigner(asigners, chain);
         }
 
         CodeSigner[] tmp = new CodeSigner[asigners.size()];
         asigners.toArray(tmp);
         return tmp;
-
     }
 
-    private void addCodeSigner(ArrayList<CodeSigner> asigners,
-            List<Certificate> list) {
+    private void addCodeSigner(ArrayList<CodeSigner> asigners, Certificate[] certs) {
+        for (Certificate cert : certs) {
+            // Only X509Certificate instances are counted. See API spec.
+            if (!(cert instanceof X509Certificate)) {
+                return;
+            }
+        }
+
         CertPath certPath = null;
         if (!isFactoryChecked) {
             try {
@@ -214,7 +230,7 @@
             return;
         }
         try {
-            certPath = factory.generateCertPath(list);
+            certPath = factory.generateCertPath(Arrays.asList(certs));
         } catch (CertificateException ex) {
             // do nothing
         }
diff --git a/luni/src/main/java/java/util/jar/JarFile.java b/luni/src/main/java/java/util/jar/JarFile.java
index a089019..6b147f6 100644
--- a/luni/src/main/java/java/util/jar/JarFile.java
+++ b/luni/src/main/java/java/util/jar/JarFile.java
@@ -196,16 +196,6 @@
      *             If the file cannot be read.
      */
     public JarFile(File file, boolean verify, int mode) throws IOException {
-        this(file, verify, mode, false);
-    }
-
-    /**
-     * See previous constructor for other parameter definitions.
-     * @param chainCheck
-     *            whether or not to check certificate chain signatures
-     * @hide
-     */
-    public JarFile(File file, boolean verify, int mode, boolean chainCheck) throws IOException {
         super(file, mode);
 
         // Step 1: Scan the central directory for meta entries (MANIFEST.mf
@@ -225,7 +215,7 @@
             // We create the manifest straight away, so that we can create
             // the jar verifier as well.
             manifest = new Manifest(metaEntries.get(MANIFEST_NAME), true);
-            verifier = new JarVerifier(getName(), manifest, metaEntries, chainCheck);
+            verifier = new JarVerifier(getName(), manifest, metaEntries);
         } else {
             verifier = null;
             manifestBytes = metaEntries.get(MANIFEST_NAME);
@@ -257,17 +247,7 @@
      *             If file cannot be opened or read.
      */
     public JarFile(String filename, boolean verify) throws IOException {
-        this(filename, verify, false);
-    }
-
-    /**
-     * See previous constructor for other parameter definitions.
-     * @param chainCheck
-     *            whether or not to check certificate chain signatures
-     * @hide
-     */
-    public JarFile(String filename, boolean verify, boolean chainCheck) throws IOException {
-        this(new File(filename), verify, ZipFile.OPEN_READ, chainCheck);
+        this(new File(filename), verify, ZipFile.OPEN_READ);
     }
 
     /**
diff --git a/luni/src/main/java/java/util/jar/JarVerifier.java b/luni/src/main/java/java/util/jar/JarVerifier.java
index f78cbe8..467e298 100644
--- a/luni/src/main/java/java/util/jar/JarVerifier.java
+++ b/luni/src/main/java/java/util/jar/JarVerifier.java
@@ -27,7 +27,6 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.cert.Certificate;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Iterator;
@@ -72,11 +71,8 @@
     private final Hashtable<String, Certificate[]> certificates =
             new Hashtable<String, Certificate[]>(5);
 
-    private final Hashtable<String, Certificate[]> verifiedEntries =
-            new Hashtable<String, Certificate[]>();
-
-    /** Whether or not to check certificate chain signatures. */
-    private final boolean chainCheck;
+    private final Hashtable<String, Certificate[][]> verifiedEntries =
+            new Hashtable<String, Certificate[][]>();
 
     /**
      * Stores and a hash and a message digest and verifies that massage digest
@@ -90,16 +86,16 @@
 
         private final byte[] hash;
 
-        private final Certificate[] certificates;
+        private final Certificate[][] certChains;
 
-        private final Hashtable<String, Certificate[]> verifiedEntries;
+        private final Hashtable<String, Certificate[][]> verifiedEntries;
 
         VerifierEntry(String name, MessageDigest digest, byte[] hash,
-                Certificate[] certificates, Hashtable<String, Certificate[]> verifedEntries) {
+                Certificate[][] certChains, Hashtable<String, Certificate[][]> verifedEntries) {
             this.name = name;
             this.digest = digest;
             this.hash = hash;
-            this.certificates = certificates;
+            this.certChains = certChains;
             this.verifiedEntries = verifedEntries;
         }
 
@@ -135,7 +131,7 @@
             if (!MessageDigest.isEqual(d, Base64.decode(hash))) {
                 throw invalidDigest(JarFile.MANIFEST_NAME, name, name);
             }
-            verifiedEntries.put(name, certificates);
+            verifiedEntries.put(name, certChains);
         }
     }
 
@@ -150,27 +146,16 @@
     }
 
     /**
-     * Convenience constructor for backward compatibility.
-     */
-    JarVerifier(String name, Manifest manifest, HashMap<String, byte[]> metaEntries) {
-        this(name, manifest, metaEntries, false);
-    }
-
-    /**
      * Constructs and returns a new instance of {@code JarVerifier}.
      *
      * @param name
      *            the name of the JAR file being verified.
-     * @param chainCheck
-     *            whether to check the certificate chain signatures
      */
-    JarVerifier(String name, Manifest manifest, HashMap<String, byte[]> metaEntries,
-            boolean chainCheck) {
+    JarVerifier(String name, Manifest manifest, HashMap<String, byte[]> metaEntries) {
         jarName = name;
         this.manifest = manifest;
         this.metaEntries = metaEntries;
         this.mainAttributesEnd = manifest.getMainAttributesEnd();
-        this.chainCheck = chainCheck;
     }
 
     /**
@@ -199,7 +184,7 @@
             return null;
         }
 
-        ArrayList<Certificate> certs = new ArrayList<Certificate>();
+        ArrayList<Certificate[]> certChains = new ArrayList<Certificate[]>();
         Iterator<Map.Entry<String, HashMap<String, Attributes>>> it = signatures.entrySet().iterator();
         while (it.hasNext()) {
             Map.Entry<String, HashMap<String, Attributes>> entry = it.next();
@@ -209,16 +194,16 @@
                 String signatureFile = entry.getKey();
                 Certificate[] certChain = certificates.get(signatureFile);
                 if (certChain != null) {
-                    Collections.addAll(certs, certChain);
+                    certChains.add(certChain);
                 }
             }
         }
 
         // entry is not signed
-        if (certs.isEmpty()) {
+        if (certChains.isEmpty()) {
             return null;
         }
-        Certificate[] certificatesArray = certs.toArray(new Certificate[certs.size()]);
+        Certificate[][] certChainsArray = certChains.toArray(new Certificate[certChains.size()][]);
 
         for (int i = 0; i < DIGEST_ALGORITHMS.length; i++) {
             final String algorithm = DIGEST_ALGORITHMS[i];
@@ -230,9 +215,8 @@
 
             try {
                 return new VerifierEntry(name, MessageDigest.getInstance(algorithm), hashBytes,
-                        certificatesArray, verifiedEntries);
-            } catch (NoSuchAlgorithmException e) {
-                // ignored
+                        certChainsArray, verifiedEntries);
+            } catch (NoSuchAlgorithmException ignored) {
             }
         }
         return null;
@@ -309,15 +293,7 @@
         try {
             Certificate[] signerCertChain = JarUtils.verifySignature(
                     new ByteArrayInputStream(sfBytes),
-                    new ByteArrayInputStream(sBlockBytes),
-                    chainCheck);
-            /*
-             * Recursive call in loading security provider related class which
-             * is in a signed JAR.
-             */
-            if (metaEntries == null) {
-                return;
-            }
+                    new ByteArrayInputStream(sBlockBytes));
             if (signerCertChain != null) {
                 certificates.put(signatureFile, signerCertChain);
             }
@@ -418,20 +394,16 @@
     }
 
     /**
-     * Returns all of the {@link java.security.cert.Certificate} instances that
+     * Returns all of the {@link java.security.cert.Certificate} chains that
      * were used to verify the signature on the JAR entry called
-     * {@code name}.
+     * {@code name}. Callers must not modify the returned arrays.
      *
      * @param name
      *            the name of a JAR entry.
-     * @return an array of {@link java.security.cert.Certificate}.
+     * @return an array of {@link java.security.cert.Certificate} chains.
      */
-    Certificate[] getCertificates(String name) {
-        Certificate[] verifiedCerts = verifiedEntries.get(name);
-        if (verifiedCerts == null) {
-            return null;
-        }
-        return verifiedCerts.clone();
+    Certificate[][] getCertificateChains(String name) {
+        return verifiedEntries.get(name);
     }
 
     /**
diff --git a/luni/src/main/java/java/util/jar/StrictJarFile.java b/luni/src/main/java/java/util/jar/StrictJarFile.java
index 80b4fe9..4a8af5f 100644
--- a/luni/src/main/java/java/util/jar/StrictJarFile.java
+++ b/luni/src/main/java/java/util/jar/StrictJarFile.java
@@ -63,7 +63,7 @@
             // or manifests, so it's best to throw as early as possible.
             HashMap<String, byte[]> metaEntries = getMetaEntries();
             this.manifest = new Manifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
-            this.verifier = new JarVerifier(fileName, manifest, metaEntries, true);
+            this.verifier = new JarVerifier(fileName, manifest, metaEntries);
 
             isSigned = verifier.readCertificates() && verifier.isSignedJar();
         } catch (IOException ioe) {
@@ -87,16 +87,51 @@
     }
 
     /**
-     * Return all certificates for a given {@link ZipEntry} belonging to this jar.
+     * Return all certificate chains for a given {@link ZipEntry} belonging to this jar.
      * This method MUST be called only after fully exhausting the InputStream belonging
      * to this entry.
      *
      * Returns {@code null} if this jar file isn't signed or if this method is
      * called before the stream is processed.
      */
+    public Certificate[][] getCertificateChains(ZipEntry ze) {
+        if (isSigned) {
+            return verifier.getCertificateChains(ze.getName());
+        }
+
+        return null;
+    }
+
+    /**
+     * Return all certificates for a given {@link ZipEntry} belonging to this jar.
+     * This method MUST be called only after fully exhausting the InputStream belonging
+     * to this entry.
+     *
+     * Returns {@code null} if this jar file isn't signed or if this method is
+     * called before the stream is processed.
+     *
+     * @deprecated Switch callers to use getCertificateChains instead
+     */
+    @Deprecated
     public Certificate[] getCertificates(ZipEntry ze) {
         if (isSigned) {
-            return verifier.getCertificates(ze.getName());
+            Certificate[][] certChains = verifier.getCertificateChains(ze.getName());
+
+            // Measure number of certs.
+            int count = 0;
+            for (Certificate[] chain : certChains) {
+                count += chain.length;
+            }
+
+            // Create new array and copy all the certs into it.
+            Certificate[] certs = new Certificate[count];
+            int i = 0;
+            for (Certificate[] chain : certChains) {
+                System.arraycopy(chain, 0, certs, i, chain.length);
+                i += chain.length;
+            }
+
+            return certs;
         }
 
         return null;
diff --git a/luni/src/main/java/javax/crypto/SealedObject.java b/luni/src/main/java/javax/crypto/SealedObject.java
index cfb970b..4b91184 100644
--- a/luni/src/main/java/javax/crypto/SealedObject.java
+++ b/luni/src/main/java/javax/crypto/SealedObject.java
@@ -19,6 +19,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
@@ -29,6 +30,7 @@
 import java.security.Key;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
+import libcore.io.IoUtils;
 
 /**
  * A {@code SealedObject} is a wrapper around a {@code serializable} object
@@ -57,14 +59,21 @@
     private String paramsAlg;
 
     private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
-        // We do unshared reads here to ensure we have our own clones of the byte[]s.
-        encodedParams = (byte[]) s.readUnshared();
-        encryptedContent = (byte[]) s.readUnshared();
-        // These are regular shared reads because the algorithms used by a given stream are
-        // almost certain to the be same for each object, and String is immutable anyway,
-        // so there's no security concern about sharing.
-        sealAlg = (String) s.readObject();
-        paramsAlg = (String) s.readObject();
+        // This implementation is based on the latest recommendations for safe deserialization at
+        // the time of writing. See the Serialization spec section A.6.
+        ObjectInputStream.GetField fields = s.readFields();
+
+        // The mutable byte arrays are cloned and the immutable strings are not.
+        this.encodedParams = getSafeCopy(fields, "encodedParams");
+        this.encryptedContent = getSafeCopy(fields, "encryptedContent");
+        this.paramsAlg = (String) fields.get("paramsAlg", null);
+        this.sealAlg = (String) fields.get("sealAlg", null);
+    }
+
+    private static byte[] getSafeCopy(ObjectInputStream.GetField fields, String fieldName)
+            throws IOException {
+        byte[] fieldValue = (byte[]) fields.get(fieldName, null);
+        return fieldValue != null ? fieldValue.clone() : null;
     }
 
     /**
@@ -87,13 +96,14 @@
      *             if the cipher is {@code null}.
      */
     public SealedObject(Serializable object, Cipher c)
-                throws IOException, IllegalBlockSizeException {
+            throws IOException, IllegalBlockSizeException {
         if (c == null) {
             throw new NullPointerException("c == null");
         }
+        ObjectOutputStream oos = null;
         try {
             ByteArrayOutputStream bos = new ByteArrayOutputStream();
-            ObjectOutputStream oos = new ObjectOutputStream(bos);
+            oos = new ObjectOutputStream(bos);
             oos.writeObject(object);
             oos.flush();
             AlgorithmParameters ap = c.getParameters();
@@ -105,6 +115,8 @@
             // should be never thrown because the cipher
             // should be initialized for encryption
             throw new IOException(e.toString());
+        } finally {
+            IoUtils.closeQuietly(oos);
         }
     }
 
@@ -119,8 +131,10 @@
         if (so == null) {
             throw new NullPointerException("so == null");
         }
-        this.encryptedContent = so.encryptedContent;
-        this.encodedParams = so.encodedParams;
+        // For safety: clone the mutable arrays so that each object has its own independent copy of
+        // the data.
+        this.encryptedContent = so.encryptedContent != null ? so.encryptedContent.clone() : null;
+        this.encodedParams = so.encodedParams != null ? so.encodedParams.clone() : null;
         this.sealAlg = so.sealAlg;
         this.paramsAlg = so.paramsAlg;
     }
@@ -158,18 +172,14 @@
         try {
             Cipher cipher = Cipher.getInstance(sealAlg);
             if ((paramsAlg != null) && (paramsAlg.length() != 0)) {
-                AlgorithmParameters params =
-                    AlgorithmParameters.getInstance(paramsAlg);
+                AlgorithmParameters params = AlgorithmParameters.getInstance(paramsAlg);
                 params.init(encodedParams);
                 cipher.init(Cipher.DECRYPT_MODE, key, params);
             } else {
                 cipher.init(Cipher.DECRYPT_MODE, key);
             }
             byte[] serialized = cipher.doFinal(encryptedContent);
-            ObjectInputStream ois =
-                    new ObjectInputStream(
-                            new ByteArrayInputStream(serialized));
-            return ois.readObject();
+            return readSerialized(serialized);
         } catch (NoSuchPaddingException e)  {
             // should not be thrown because cipher text was made
             // with existing padding
@@ -186,7 +196,7 @@
             // should not be thrown because the cipher text
             // was correctly made
             throw new NoSuchAlgorithmException(e.toString());
-        } catch (IllegalStateException  e) {
+        } catch (IllegalStateException e) {
             // should never be thrown because cipher is initialized
             throw new NoSuchAlgorithmException(e.toString());
         }
@@ -217,10 +227,7 @@
             throw new NullPointerException("c == null");
         }
         byte[] serialized = c.doFinal(encryptedContent);
-        ObjectInputStream ois =
-                new ObjectInputStream(
-                        new ByteArrayInputStream(serialized));
-        return ois.readObject();
+        return readSerialized(serialized);
     }
 
     /**
@@ -253,18 +260,14 @@
         try {
             Cipher cipher = Cipher.getInstance(sealAlg, provider);
             if ((paramsAlg != null) && (paramsAlg.length() != 0)) {
-                AlgorithmParameters params =
-                    AlgorithmParameters.getInstance(paramsAlg);
+                AlgorithmParameters params = AlgorithmParameters.getInstance(paramsAlg);
                 params.init(encodedParams);
                 cipher.init(Cipher.DECRYPT_MODE, key, params);
             } else {
                 cipher.init(Cipher.DECRYPT_MODE, key);
             }
             byte[] serialized = cipher.doFinal(encryptedContent);
-            ObjectInputStream ois =
-                    new ObjectInputStream(
-                            new ByteArrayInputStream(serialized));
-            return ois.readObject();
+            return readSerialized(serialized);
         } catch (NoSuchPaddingException e)  {
             // should not be thrown because cipher text was made
             // with existing padding
@@ -286,4 +289,15 @@
             throw new NoSuchAlgorithmException(e.toString());
         }
     }
+
+    private static Object readSerialized(byte[] serialized)
+            throws IOException, ClassNotFoundException {
+        ObjectInputStream ois = null;
+        try {
+            ois = new ObjectInputStream(new ByteArrayInputStream(serialized));
+            return ois.readObject();
+        } finally {
+            IoUtils.closeQuietly(ois);
+        }
+    }
 }
diff --git a/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java b/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
index fa11371..65c8b03 100644
--- a/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
+++ b/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
@@ -154,10 +154,7 @@
         int suffixLength = cn.length() - (asterisk + 1);
         int suffixStart = hostName.length() - suffixLength;
         if (hostName.indexOf('.', asterisk) < suffixStart) {
-            // TODO: remove workaround for *.clients.google.com http://b/5426333
-            if (!hostName.endsWith(".clients.google.com")) {
-                return false; // wildcard '*' can't match a '.'
-            }
+            return false; // wildcard '*' can't match a '.'
         }
 
         if (!hostName.regionMatches(suffixStart, cn, asterisk + 1, suffixLength)) {
diff --git a/luni/src/main/java/javax/net/ssl/SSLEngine.java b/luni/src/main/java/javax/net/ssl/SSLEngine.java
index 59cdf62..3ba3450 100644
--- a/luni/src/main/java/javax/net/ssl/SSLEngine.java
+++ b/luni/src/main/java/javax/net/ssl/SSLEngine.java
@@ -542,14 +542,14 @@
  *             <td>20+</td>
  *         </tr>
  *         <tr>
- *             <td>TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256</td>
- *             <td>20+</td>
- *             <td></td>
+ *             <td>TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA</td>
+ *             <td>21+</td>
+ *             <td>21+</td>
  *         </tr>
  *         <tr>
- *             <td>TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384</td>
- *             <td>20+</td>
- *             <td></td>
+ *             <td>TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA</td>
+ *             <td>21+</td>
+ *             <td>21+</td>
  *         </tr>
  *         <tr>
  *             <td>TLS_NULL_WITH_NULL_NULL</td>
@@ -558,22 +558,22 @@
  *         </tr>
  *         <tr>
  *             <td>TLS_PSK_WITH_3DES_EDE_CBC_SHA</td>
- *             <td>20+</td>
+ *             <td>21+</td>
  *             <td></td>
  *         </tr>
  *         <tr>
  *             <td>TLS_PSK_WITH_AES_128_CBC_SHA</td>
- *             <td>20+</td>
- *             <td></td>
+ *             <td>21+</td>
+ *             <td>21+</td>
  *         </tr>
  *         <tr>
  *             <td>TLS_PSK_WITH_AES_256_CBC_SHA</td>
- *             <td>20+</td>
- *             <td></td>
+ *             <td>21+</td>
+ *             <td>21+</td>
  *         </tr>
  *         <tr>
  *             <td>TLS_PSK_WITH_RC4_128_SHA</td>
- *             <td>20+</td>
+ *             <td>21+</td>
  *             <td></td>
  *         </tr>
  *         <tr>
@@ -639,6 +639,9 @@
  *     </tbody>
  * </table>
  *
+ * <p><em>NOTE</em>: PSK cipher suites are enabled by default only if the {@code SSLContext} through
+ * which the engine was created has been initialized with a {@code PSKKeyManager}.
+ *
  * @since 1.5
  */
 public abstract class SSLEngine {
diff --git a/luni/src/main/java/javax/net/ssl/SSLServerSocketFactory.java b/luni/src/main/java/javax/net/ssl/SSLServerSocketFactory.java
index cce72cd..03b8828 100644
--- a/luni/src/main/java/javax/net/ssl/SSLServerSocketFactory.java
+++ b/luni/src/main/java/javax/net/ssl/SSLServerSocketFactory.java
@@ -20,6 +20,7 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.Security;
 import javax.net.ServerSocketFactory;
+import org.apache.harmony.security.fortress.Services;
 
 /**
  * The factory for SSL server sockets.
@@ -32,6 +33,8 @@
 
     private static String defaultName;
 
+    private static int lastCacheVersion = -1;
+
     /**
      * Returns the default {@code SSLServerSocketFactory} instance. The default
      * implementation is defined by the security property
@@ -40,6 +43,12 @@
      * @return the default {@code SSLServerSocketFactory} instance.
      */
     public static synchronized ServerSocketFactory getDefault() {
+        int newCacheVersion = Services.getCacheVersion();
+        if (lastCacheVersion != newCacheVersion) {
+            defaultServerSocketFactory = null;
+            defaultName = null;
+            lastCacheVersion = newCacheVersion;
+        }
         if (defaultServerSocketFactory != null) {
             return defaultServerSocketFactory;
         }
diff --git a/luni/src/main/java/javax/net/ssl/SSLSocket.java b/luni/src/main/java/javax/net/ssl/SSLSocket.java
index 72e1dbe..33a88b6 100644
--- a/luni/src/main/java/javax/net/ssl/SSLSocket.java
+++ b/luni/src/main/java/javax/net/ssl/SSLSocket.java
@@ -346,14 +346,14 @@
  *             <td>11+</td>
  *         </tr>
  *         <tr>
- *             <td>TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256</td>
- *             <td>20+</td>
- *             <td></td>
+ *             <td>TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA</td>
+ *             <td>21+</td>
+ *             <td>21+</td>
  *         </tr>
  *         <tr>
- *             <td>TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384</td>
- *             <td>20+</td>
- *             <td></td>
+ *             <td>TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA</td>
+ *             <td>21+</td>
+ *             <td>21+</td>
  *         </tr>
  *         <tr>
  *             <td>TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA</td>
@@ -522,22 +522,22 @@
  *         </tr>
  *         <tr>
  *             <td>TLS_PSK_WITH_3DES_EDE_CBC_SHA</td>
- *             <td>20+</td>
+ *             <td>21+</td>
  *             <td></td>
  *         </tr>
  *         <tr>
  *             <td>TLS_PSK_WITH_AES_128_CBC_SHA</td>
- *             <td>20+</td>
- *             <td></td>
+ *             <td>21+</td>
+ *             <td>21+</td>
  *         </tr>
  *         <tr>
  *             <td>TLS_PSK_WITH_AES_256_CBC_SHA</td>
- *             <td>20+</td>
- *             <td></td>
+ *             <td>21+</td>
+ *             <td>21+</td>
  *         </tr>
  *         <tr>
  *             <td>TLS_PSK_WITH_RC4_128_SHA</td>
- *             <td>20+</td>
+ *             <td>21+</td>
  *             <td></td>
  *         </tr>
  *         <tr>
@@ -578,6 +578,9 @@
  *     </tbody>
  * </table>
  *
+ * <p><em>NOTE</em>: PSK cipher suites are enabled by default only if the {@code SSLContext} through
+ * which the socket was created has been initialized with a {@code PSKKeyManager}.
+ *
  * <p>API Levels 1 to 8 use OpenSSL names for cipher suites. The table below
  * lists these OpenSSL names and their corresponding standard names used in API
  * Levels 9 and newer.
diff --git a/luni/src/main/java/javax/net/ssl/SSLSocketFactory.java b/luni/src/main/java/javax/net/ssl/SSLSocketFactory.java
index b07d0fd..b506fa62 100644
--- a/luni/src/main/java/javax/net/ssl/SSLSocketFactory.java
+++ b/luni/src/main/java/javax/net/ssl/SSLSocketFactory.java
@@ -22,6 +22,7 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.Security;
 import javax.net.SocketFactory;
+import org.apache.harmony.security.fortress.Services;
 
 /**
  * The abstract factory implementation to create {@code SSLSocket}s.
@@ -32,7 +33,7 @@
     // The default SSL socket factory
     private static SocketFactory defaultSocketFactory;
 
-    private static String defaultName;
+    private static int lastCacheVersion = -1;
 
     /**
      * Returns the default {@code SSLSocketFactory} instance. The default is
@@ -41,23 +42,39 @@
      * @return the default ssl socket factory instance.
      */
     public static synchronized SocketFactory getDefault() {
-        if (defaultSocketFactory != null) {
+        int newCacheVersion = Services.getCacheVersion();
+        if (defaultSocketFactory != null && lastCacheVersion == newCacheVersion) {
             return defaultSocketFactory;
         }
-        if (defaultName == null) {
-            defaultName = Security.getProperty("ssl.SocketFactory.provider");
-            if (defaultName != null) {
-                ClassLoader cl = Thread.currentThread().getContextClassLoader();
-                if (cl == null) {
-                    cl = ClassLoader.getSystemClassLoader();
-                }
-                try {
-                    final Class<?> sfc = Class.forName(defaultName, true, cl);
-                    defaultSocketFactory = (SocketFactory) sfc.newInstance();
-                } catch (Exception e) {
-                    System.logE("Problem creating " + defaultName, e);
+        lastCacheVersion = newCacheVersion;
+
+        String newName = Security.getProperty("ssl.SocketFactory.provider");
+        if (newName != null) {
+            /* The cache could have been invalidated, but the provider name didn't change. This
+             * will be the most common state, so check for it early without resetting the default
+             * SocketFactory.
+             */
+            if (defaultSocketFactory != null) {
+                if (newName.equals(defaultSocketFactory.getClass().getName())) {
+                    return defaultSocketFactory;
+                } else {
+                    defaultSocketFactory = null;
                 }
             }
+
+            ClassLoader cl = Thread.currentThread().getContextClassLoader();
+            if (cl == null) {
+                cl = ClassLoader.getSystemClassLoader();
+            }
+            try {
+                final Class<?> sfc = Class.forName(newName, true, cl);
+                defaultSocketFactory = (SocketFactory) sfc.newInstance();
+            } catch (Exception e) {
+                System.logW("Could not create " + newName + " with ClassLoader "
+                        + cl.toString() + ": " + e.getMessage());
+            }
+        } else {
+            defaultSocketFactory = null;
         }
 
         if (defaultSocketFactory == null) {
@@ -71,10 +88,12 @@
                 defaultSocketFactory = context.getSocketFactory();
             }
         }
+
         if (defaultSocketFactory == null) {
             // Use internal implementation
             defaultSocketFactory = new DefaultSSLSocketFactory("No SSLSocketFactory installed");
         }
+
         return defaultSocketFactory;
     }
 
diff --git a/luni/src/main/java/libcore/icu/ICU.java b/luni/src/main/java/libcore/icu/ICU.java
index 33c899e..06e2205 100644
--- a/luni/src/main/java/libcore/icu/ICU.java
+++ b/luni/src/main/java/libcore/icu/ICU.java
@@ -58,18 +58,6 @@
     return isoCountries.clone();
   }
 
-  public static Locale forLanguageTag(String languageTag, boolean strict) {
-    final String icuLocaleId = localeForLanguageTag(languageTag, strict);
-    if (icuLocaleId == null) {
-      // TODO: We should probably return "und" here. From what I can tell,
-      // this happens only when the language in the languageTag is bad.
-      // Investigate this a bit more.
-      return null;
-    }
-
-    return localeFromIcuLocaleId(icuLocaleId);
-  }
-
   private static final int IDX_LANGUAGE = 0;
   private static final int IDX_SCRIPT = 1;
   private static final int IDX_REGION = 2;
@@ -444,9 +432,7 @@
   private static native String[] getISOLanguagesNative();
   private static native String[] getISOCountriesNative();
 
-  private static native String localeForLanguageTag(String languageTag, boolean strict);
-
-  static native boolean initLocaleDataNative(String locale, LocaleData result);
+  static native boolean initLocaleDataNative(String languageTag, LocaleData result);
 
   /**
    * Takes a BCP-47 language tag (Locale.toLanguageTag()). e.g. en-US, not en_US
diff --git a/luni/src/main/java/libcore/icu/LocaleData.java b/luni/src/main/java/libcore/icu/LocaleData.java
index 845ba32..ec05b53 100644
--- a/luni/src/main/java/libcore/icu/LocaleData.java
+++ b/luni/src/main/java/libcore/icu/LocaleData.java
@@ -112,27 +112,36 @@
     private LocaleData() {
     }
 
+    public static Locale mapInvalidAndNullLocales(Locale locale) {
+        if (locale == null) {
+            return Locale.getDefault();
+        }
+
+        if ("und".equals(locale.toLanguageTag())) {
+            return Locale.ROOT;
+        }
+
+        return locale;
+    }
+
     /**
      * Returns a shared LocaleData for the given locale.
      */
     public static LocaleData get(Locale locale) {
-        if (locale == null) {
-            locale = Locale.getDefault();
-        }
-        String localeName = locale.toString();
+        final String languageTag = locale.toLanguageTag();
         synchronized (localeDataCache) {
-            LocaleData localeData = localeDataCache.get(localeName);
+            LocaleData localeData = localeDataCache.get(languageTag);
             if (localeData != null) {
                 return localeData;
             }
         }
         LocaleData newLocaleData = initLocaleData(locale);
         synchronized (localeDataCache) {
-            LocaleData localeData = localeDataCache.get(localeName);
+            LocaleData localeData = localeDataCache.get(languageTag);
             if (localeData != null) {
                 return localeData;
             }
-            localeDataCache.put(localeName, newLocaleData);
+            localeDataCache.put(languageTag, newLocaleData);
             return newLocaleData;
         }
     }
@@ -171,7 +180,7 @@
 
     private static LocaleData initLocaleData(Locale locale) {
         LocaleData localeData = new LocaleData();
-        if (!ICU.initLocaleDataNative(locale.toString(), localeData)) {
+        if (!ICU.initLocaleDataNative(locale.toLanguageTag(), localeData)) {
             throw new AssertionError("couldn't initialize LocaleData for locale " + locale);
         }
 
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index d09e442..bf4b448 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -52,6 +52,7 @@
 
     public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException { return os.accept(fd, peerAddress); }
     public boolean access(String path, int mode) throws ErrnoException { return os.access(path, mode); }
+    public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException { return os.android_getaddrinfo(node, hints, netId); }
     public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.bind(fd, address, port); }
     public void chmod(String path, int mode) throws ErrnoException { os.chmod(path, mode); }
     public void chown(String path, int uid, int gid) throws ErrnoException { os.chown(path, uid, gid); }
@@ -73,7 +74,6 @@
     public void fsync(FileDescriptor fd) throws ErrnoException { os.fsync(fd); }
     public void ftruncate(FileDescriptor fd, long length) throws ErrnoException { os.ftruncate(fd, length); }
     public String gai_strerror(int error) { return os.gai_strerror(error); }
-    public InetAddress[] getaddrinfo(String node, StructAddrinfo hints) throws GaiException { return os.getaddrinfo(node, hints); }
     public int getegid() { return os.getegid(); }
     public int geteuid() { return os.geteuid(); }
     public int getgid() { return os.getgid(); }
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index a537aeb..511bb27 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -43,6 +43,7 @@
 public interface Os {
     public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException;
     public boolean access(String path, int mode) throws ErrnoException;
+    public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException;
     public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
     public void chmod(String path, int mode) throws ErrnoException;
     public void chown(String path, int uid, int gid) throws ErrnoException;
@@ -64,7 +65,6 @@
     public void fsync(FileDescriptor fd) throws ErrnoException;
     public void ftruncate(FileDescriptor fd, long length) throws ErrnoException;
     public String gai_strerror(int error);
-    public InetAddress[] getaddrinfo(String node, StructAddrinfo hints) throws GaiException;
     public int getegid();
     public int geteuid();
     public int getgid();
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index 7551190..f5eaaa3 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -46,6 +46,7 @@
 
     public native FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException;
     public native boolean access(String path, int mode) throws ErrnoException;
+    public native InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException;
     public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
     public native void chmod(String path, int mode) throws ErrnoException;
     public native void chown(String path, int uid, int gid) throws ErrnoException;
@@ -67,7 +68,6 @@
     public native void fsync(FileDescriptor fd) throws ErrnoException;
     public native void ftruncate(FileDescriptor fd, long length) throws ErrnoException;
     public native String gai_strerror(int error);
-    public native InetAddress[] getaddrinfo(String node, StructAddrinfo hints) throws GaiException;
     public native int getegid();
     public native int geteuid();
     public native int getgid();
diff --git a/luni/src/main/java/libcore/util/ZoneInfo.java b/luni/src/main/java/libcore/util/ZoneInfo.java
index 54ee667..fbd120b 100644
--- a/luni/src/main/java/libcore/util/ZoneInfo.java
+++ b/luni/src/main/java/libcore/util/ZoneInfo.java
@@ -13,11 +13,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
+/*
+ * Elements of the WallTime class are a port of Bionic's localtime.c to Java. That code had the
+ * following header:
+ *
+ * This file is in the public domain, so clarified as of
+ * 1996-06-05 by Arthur David Olson.
+ */
 package libcore.util;
 
 import java.util.Arrays;
+import java.util.Calendar;
 import java.util.Date;
+import java.util.GregorianCalendar;
 import java.util.TimeZone;
 import libcore.io.BufferIterator;
 
@@ -51,7 +59,7 @@
     private final byte[] mTypes;
     private final byte[] mIsDsts;
 
-    public static TimeZone makeTimeZone(String id, BufferIterator it) {
+    public static ZoneInfo makeTimeZone(String id, BufferIterator it) {
         // Variable names beginning tzh_ correspond to those in "tzfile.h".
 
         // Check tzh_magic.
@@ -301,4 +309,664 @@
             ",transitions=" + mTransitions.length +
             "]";
     }
+
+    @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();
+    }
+
+    /**
+     * 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.
+     */
+    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;
+
+        public WallTime() {
+            this.calendar = new GregorianCalendar(false);
+            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.
+         */
+        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 {
+                    // transitionIndex can be in the range -1..zoneInfo.mTransitions.length - 1
+                    int transitionIndex = findTransitionIndex(zoneInfo, timeSeconds);
+                    if (transitionIndex < 0) {
+                        // -1 means timeSeconds is "before the first recorded transition". The first
+                        // recorded transition is treated as a transition from non-DST and the raw
+                        // offset.
+                        isDst = 0;
+                    } else {
+                        byte transitionType = zoneInfo.mTypes[transitionIndex];
+                        offsetSeconds += zoneInfo.mOffsets[transitionType];
+                        isDst = zoneInfo.mIsDsts[transitionType];
+                    }
+                }
+
+                // Perform arithmetic that might underflow before setting fields.
+                int wallTimeSeconds = checkedAdd(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.
+         */
+        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 = checkedSubtract(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 = findTransitionIndex(zoneInfo, 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 = checkedAdd(oldWallTimeSeconds, adjustmentSeconds);
+                if (targetInterval.containsWallTime(adjustedWallTimeSeconds)) {
+                    // Perform any arithmetic that might overflow.
+                    int returnValue = checkedSubtract(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;
+                }
+                byte type = zoneInfo.mTypes[transitionIndex];
+                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 = checkedSubtract(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;
+        }
+
+        public void setYear(int year) {
+            this.year = year;
+        }
+
+        public void setMonth(int month) {
+            this.month = month;
+        }
+
+        public void setMonthDay(int monthDay) {
+            this.monthDay = monthDay;
+        }
+
+        public void setHour(int hour) {
+            this.hour = hour;
+        }
+
+        public void setMinute(int minute) {
+            this.minute = minute;
+        }
+
+        public void setSecond(int second) {
+            this.second = second;
+        }
+
+        public void setWeekDay(int weekDay) {
+            this.weekDay = weekDay;
+        }
+
+        public void setYearDay(int yearDay) {
+            this.yearDay = yearDay;
+        }
+
+        public void setIsDst(int isDst) {
+            this.isDst = isDst;
+        }
+
+        public void setGmtOffset(int gmtoff) {
+            this.gmtOffsetSeconds = gmtoff;
+        }
+
+        public int getYear() {
+            return year;
+        }
+
+        public int getMonth() {
+            return month;
+        }
+
+        public int getMonthDay() {
+            return monthDay;
+        }
+
+        public int getHour() {
+            return hour;
+        }
+
+        public int getMinute() {
+            return minute;
+        }
+
+        public int getSecond() {
+            return second;
+        }
+
+        public int getWeekDay() {
+            return weekDay;
+        }
+
+        public int getYearDay() {
+            return yearDay;
+        }
+
+        public int getGmtOffset() {
+            return gmtOffsetSeconds;
+        }
+
+        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);
+        }
+
+        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;
+        }
+
+        /**
+         * Find the transition in the {@code timezone} in effect at {@code timeSeconds}.
+         *
+         * <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.
+         */
+        private static int findTransitionIndex(ZoneInfo timeZone, int timeSeconds) {
+            int matchingRawTransition = Arrays.binarySearch(timeZone.mTransitions, timeSeconds);
+            if (matchingRawTransition < 0) {
+                matchingRawTransition = ~matchingRawTransition - 1;
+            }
+            return matchingRawTransition;
+        }
+    }
+
+    /**
+     * 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>For convenience all wall-time values are represented as the number of seconds since the
+     * beginning of the Unix epoch <em>in UTC</em>. To convert from a wall-time to the actual time
+     * in the offset 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 {
+
+        private final int startWallTimeSeconds;
+        private final int endWallTimeSeconds;
+        private final int isDst;
+        private final int totalOffsetSeconds;
+
+        /**
+         * Creates an {@link OffsetInterval}.
+         *
+         * <p>If {@code transitionIndex} is -1, the transition is synthesized to be a non-DST offset
+         * that runs from the beginning of time until the first transition in {@code timeZone} and
+         * has an offset of {@code timezone.mRawOffset}. If {@code transitionIndex} is the last
+         * transition that transition is considered to run until the end of representable time.
+         * Otherwise, the information is extracted from {@code timeZone.mTransitions},
+         * {@code timeZone.mOffsets} an {@code timeZone.mIsDsts}.
+         */
+        public static OffsetInterval create(ZoneInfo timeZone, int transitionIndex)
+                throws CheckedArithmeticException {
+
+            if (transitionIndex < -1 || transitionIndex >= timeZone.mTransitions.length) {
+                return null;
+            }
+
+            int rawOffsetSeconds = timeZone.mRawOffset / 1000;
+            if (transitionIndex == -1) {
+                int endWallTimeSeconds = checkedAdd(timeZone.mTransitions[0], rawOffsetSeconds);
+                return new OffsetInterval(Integer.MIN_VALUE, endWallTimeSeconds, 0 /* isDst */,
+                        rawOffsetSeconds);
+            }
+
+            byte type = timeZone.mTypes[transitionIndex];
+            int totalOffsetSeconds = timeZone.mOffsets[type] + rawOffsetSeconds;
+            int endWallTimeSeconds;
+            if (transitionIndex == timeZone.mTransitions.length - 1) {
+                // If this is the last transition, make up the end time.
+                endWallTimeSeconds = Integer.MAX_VALUE;
+            } else {
+                endWallTimeSeconds = checkedAdd(timeZone.mTransitions[transitionIndex + 1],
+                        totalOffsetSeconds);
+            }
+            int isDst = timeZone.mIsDsts[type];
+            int startWallTimeSeconds =
+                    checkedAdd(timeZone.mTransitions[transitionIndex], totalOffsetSeconds);
+            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).
+     *
+     * @throws CheckedArithmeticException if overflow or underflow occurs
+     */
+    private static int checkedAdd(int a, int b) throws CheckedArithmeticException {
+        // Adapted from Guava IntMath.checkedAdd();
+        long result = (long) a + b;
+        if (result != (int) result) {
+            throw new CheckedArithmeticException();
+        }
+        return (int) result;
+    }
+
+    /**
+     * Calculate (a - b).
+     *
+     * @throws CheckedArithmeticException if overflow or underflow occurs
+     */
+    private static int checkedSubtract(int a, int b) throws CheckedArithmeticException {
+        // Adapted from Guava IntMath.checkedSubtract();
+        long result = (long) a - b;
+        if (result != (int) result) {
+            throw new CheckedArithmeticException();
+        }
+        return (int) result;
+    }
 }
diff --git a/luni/src/main/java/libcore/util/ZoneInfoDB.java b/luni/src/main/java/libcore/util/ZoneInfoDB.java
index 7ff377c..07aaf04 100644
--- a/luni/src/main/java/libcore/util/ZoneInfoDB.java
+++ b/luni/src/main/java/libcore/util/ZoneInfoDB.java
@@ -68,6 +68,28 @@
     private int[] byteOffsets;
     private int[] rawUtcOffsets;
 
+    /**
+     * 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) {
+          // Work out where in the big data file this time zone is.
+          int index = Arrays.binarySearch(ids, id);
+          if (index < 0) {
+              return null;
+          }
+
+          BufferIterator it = mappedFile.bigEndianIterator();
+          it.skip(byteOffsets[index]);
+
+          return ZoneInfo.makeTimeZone(id, it);
+      }
+    };
+
     public TzData(String... paths) {
       for (String path : paths) {
         if (loadData(path)) {
@@ -206,17 +228,10 @@
       return zoneTab;
     }
 
-    public TimeZone makeTimeZone(String id) throws IOException {
-      // Work out where in the big data file this time zone is.
-      int index = Arrays.binarySearch(ids, id);
-      if (index < 0) {
-        return null;
-      }
-
-      BufferIterator it = mappedFile.bigEndianIterator();
-      it.skip(byteOffsets[index]);
-
-      return ZoneInfo.makeTimeZone(id, it);
+    public ZoneInfo makeTimeZone(String id) throws IOException {
+      ZoneInfo zoneInfo = cache.get(id);
+      // The object from the cache is cloned because TimeZone / ZoneInfo are mutable.
+      return zoneInfo == null ? null : (ZoneInfo) zoneInfo.clone();
     }
   }
 
diff --git a/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java b/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java
index b9056353..d11c8dd 100644
--- a/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java
+++ b/luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java
@@ -54,27 +54,18 @@
         new int[] {1, 2, 840, 113549, 1, 9, 4};
 
     /**
-     * @see #verifySignature(InputStream, InputStream, boolean)
-     */
-    public static Certificate[] verifySignature(InputStream signature, InputStream signatureBlock)
-            throws IOException, GeneralSecurityException {
-        return verifySignature(signature, signatureBlock, false);
-    }
-
-    /**
      * This method handle all the work with  PKCS7, ASN1 encoding, signature verifying,
      * and certification path building.
      * See also PKCS #7: Cryptographic Message Syntax Standard:
      * http://www.ietf.org/rfc/rfc2315.txt
      * @param signature - the input stream of signature file to be verified
      * @param signatureBlock - the input stream of corresponding signature block file
-     * @param chainCheck - whether to validate certificate chain signatures
      * @return array of certificates used to verify the signature file
      * @throws IOException - if some errors occurs during reading from the stream
      * @throws GeneralSecurityException - if signature verification process fails
      */
     public static Certificate[] verifySignature(InputStream signature, InputStream
-            signatureBlock, boolean chainCheck) throws IOException, GeneralSecurityException {
+            signatureBlock) throws IOException, GeneralSecurityException {
 
         BerInputStream bis = new BerInputStream(signatureBlock);
         ContentInfo info = (ContentInfo)ContentInfo.ASN1.decode(bis);
@@ -232,11 +223,11 @@
             throw new SecurityException("Incorrect signature");
         }
 
-        return createChain(certs[issuerSertIndex], certs, chainCheck);
+        return createChain(certs[issuerSertIndex], certs);
     }
 
     private static X509Certificate[] createChain(X509Certificate signer,
-            X509Certificate[] candidates, boolean chainCheck) {
+            X509Certificate[] candidates) {
         Principal issuer = signer.getIssuerDN();
 
         // Signer is self-signed
@@ -248,11 +239,10 @@
         chain.add(0, signer);
 
         X509Certificate issuerCert;
-        X509Certificate subjectCert = signer;
         int count = 1;
         while (true) {
-            issuerCert = findCert(issuer, candidates, subjectCert, chainCheck);
-            if( issuerCert == null) {
+            issuerCert = findCert(issuer, candidates);
+            if (issuerCert == null) {
                 break;
             }
             chain.add(issuerCert);
@@ -261,22 +251,13 @@
             if (issuerCert.getSubjectDN().equals(issuer)) {
                 break;
             }
-            subjectCert = issuerCert;
         }
         return chain.toArray(new X509Certificate[count]);
     }
 
-    private static X509Certificate findCert(Principal issuer, X509Certificate[] candidates,
-            X509Certificate subjectCert, boolean chainCheck) {
+    private static X509Certificate findCert(Principal issuer, X509Certificate[] candidates) {
         for (int i = 0; i < candidates.length; i++) {
             if (issuer.equals(candidates[i].getSubjectDN())) {
-                if (chainCheck) {
-                    try {
-                        subjectCert.verify(candidates[i].getPublicKey());
-                    } catch (Exception e) {
-                        continue;
-                    }
-                }
                 return candidates[i];
             }
         }
diff --git a/luni/src/main/native/Portability.h b/luni/src/main/native/Portability.h
index fb60ed4..1520311 100644
--- a/luni/src/main/native/Portability.h
+++ b/luni/src/main/native/Portability.h
@@ -65,7 +65,7 @@
 #include <sys/param.h>
 #include <sys/mount.h>
 
-#else
+#else  // defined(__APPLE__)
 
 // Bionic or glibc.
 
@@ -73,6 +73,15 @@
 #include <sys/sendfile.h>
 #include <sys/statvfs.h>
 
-#endif
+#endif  // defined(__APPLE__)
+
+#if !defined(__BIONIC__)
+#include <netdb.h>
+#include "../../bionic/libc/dns/include/resolv_netid.h"
+inline int android_getaddrinfofornet(const char *hostname, const char *servname,
+    const struct addrinfo *hints, unsigned /*netid*/, unsigned /*mark*/, struct addrinfo **res) {
+  return getaddrinfo(hostname, servname, hints, res);
+}
+#endif  // !defined(__BIONIC__)
 
 #endif  // PORTABILITY_H_included
diff --git a/luni/src/main/native/android_system_OsConstants.cpp b/luni/src/main/native/android_system_OsConstants.cpp
index 87ebc21..92212b9 100644
--- a/luni/src/main/native/android_system_OsConstants.cpp
+++ b/luni/src/main/native/android_system_OsConstants.cpp
@@ -382,6 +382,12 @@
     initConstant(env, c, "POLLRDNORM", POLLRDNORM);
     initConstant(env, c, "POLLWRBAND", POLLWRBAND);
     initConstant(env, c, "POLLWRNORM", POLLWRNORM);
+#if defined(PR_GET_DUMPABLE)
+    initConstant(env, c, "PR_GET_DUMPABLE", PR_GET_DUMPABLE);
+#endif
+#if defined(PR_SET_DUMPABLE)
+    initConstant(env, c, "PR_SET_DUMPABLE", PR_SET_DUMPABLE);
+#endif
 #if defined(PR_SET_NO_NEW_PRIVS)
     initConstant(env, c, "PR_SET_NO_NEW_PRIVS", PR_SET_NO_NEW_PRIVS);
 #endif
diff --git a/luni/src/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp
index 163d19c..df26b39 100644
--- a/luni/src/main/native/libcore_icu_ICU.cpp
+++ b/luni/src/main/native/libcore_icu_ICU.cpp
@@ -116,56 +116,6 @@
   return env->NewStringUTF(icuLocale.locale().getScript());
 }
 
-static jstring ICU_localeForLanguageTag(JNIEnv* env, jclass, jstring languageTag, jboolean strict) {
-    ScopedUtfChars languageTagChars(env, languageTag);
-    if (languageTagChars.c_str() == NULL) {
-      return NULL;
-    }
-
-    // Naively assume that in the average case, the size of
-    // the normalized language tag will be very nearly the same as the
-    // size of the input. This is generally true for language
-    // tags that are "simple" language-region-variant combinations
-    // that don't contain any grandfathered tags.
-    const size_t initialBufferSize = languageTagChars.size() + 32;
-    std::vector<char> buffer(initialBufferSize);
-    int32_t parsedLength = 0;
-
-    UErrorCode status = U_ZERO_ERROR;
-    size_t outputLength = uloc_forLanguageTag(languageTagChars.c_str(), &buffer[0],
-                                              buffer.size(), &parsedLength, &status);
-    // Note that we always allocate 1 char more than ICU asks us for,
-    // so that we can cleanly assert that it didn't overflow after the
-    // second call to uloc_forLanguageTag.
-    if (status == U_STRING_NOT_TERMINATED_WARNING) {
-        const size_t unterminated_size = buffer.size();
-        buffer.resize(unterminated_size + 1);
-        buffer[unterminated_size] = '\0';
-    } else if (status == U_BUFFER_OVERFLOW_ERROR) {
-        buffer.resize(outputLength + 1);
-        status = U_ZERO_ERROR;
-        outputLength = uloc_forLanguageTag(languageTagChars.c_str(), &buffer[0], buffer.size(),
-                                           &parsedLength, &status);
-    }
-
-    if (U_FAILURE(status) || outputLength >= buffer.size()) {
-        return NULL;
-    }
-
-    // By default, ICU will ignore all subtags starting at the first unparseable
-    // or invalid subtag. Our "strict" mode is specified to throw an error if
-    // that happens.
-    //
-    // NOTE: The cast is safe because parsedLength can never be negative thanks
-    // to the check above. ICU does not document any negative return values for
-    // that field, but check for it anyway.
-    if ((strict == JNI_TRUE) && (static_cast<uint32_t>(parsedLength) != languageTagChars.size())) {
-        return NULL;
-    }
-
-    return env->NewStringUTF(&buffer[0]);
-}
-
 static jint ICU_getCurrencyFractionDigits(JNIEnv* env, jclass, jstring javaCurrencyCode) {
   ScopedJavaUnicodeString currencyCode(env, javaCurrencyCode);
   if (!currencyCode.valid()) {
@@ -563,16 +513,16 @@
   return true;
 }
 
-static jboolean ICU_initLocaleDataNative(JNIEnv* env, jclass, jstring javaLocaleName, jobject localeData) {
-    ScopedUtfChars localeName(env, javaLocaleName);
-    if (localeName.c_str() == NULL) {
+static jboolean ICU_initLocaleDataNative(JNIEnv* env, jclass, jstring javaLanguageTag, jobject localeData) {
+    ScopedUtfChars languageTag(env, javaLanguageTag);
+    if (languageTag.c_str() == NULL) {
         return JNI_FALSE;
     }
-    if (localeName.size() >= ULOC_FULLNAME_CAPACITY) {
+    if (languageTag.size() >= ULOC_FULLNAME_CAPACITY) {
         return JNI_FALSE; // ICU has a fixed-length limit.
     }
 
-    ScopedIcuLocale icuLocale(env, javaLocaleName);
+    ScopedIcuLocale icuLocale(env, javaLanguageTag);
     if (!icuLocale.valid()) {
       return JNI_FALSE;
     }
@@ -580,27 +530,27 @@
     // Get the DateTimePatterns.
     UErrorCode status = U_ZERO_ERROR;
     bool foundDateTimePatterns = false;
-    for (LocaleNameIterator it(localeName.c_str(), status); it.HasNext(); it.Up()) {
+    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", localeName.c_str());
+        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(localeName.c_str(), status); it.HasNext(); it.Up()) {
+    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", localeName.c_str());
+      ALOGE("Couldn't find ICU yesterday/today/tomorrow for %s", languageTag.c_str());
       return JNI_FALSE;
     }
 
@@ -671,14 +621,14 @@
     setNumberPatterns(env, localeData, icuLocale.locale());
     setDecimalFormatSymbolsData(env, localeData, icuLocale.locale());
 
-    jstring countryCode = env->NewStringUTF(Locale::createFromName(localeName.c_str()).getCountry());
+    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 = ICU_getCurrencySymbol(env, NULL, javaLocaleName, internationalCurrencySymbol);
+        currencySymbol = ICU_getCurrencySymbol(env, NULL, javaLanguageTag, internationalCurrencySymbol);
     } else {
         internationalCurrencySymbol = env->NewStringUTF("XXX");
     }
@@ -820,7 +770,6 @@
     NATIVE_METHOD(ICU, getIcuVersion, "()Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getScript, "(Ljava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getUnicodeVersion, "()Ljava/lang/String;"),
-    NATIVE_METHOD(ICU, localeForLanguageTag, "(Ljava/lang/String;Z)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, toLowerCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index 51dd8a1..e8e8efb 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -25,6 +25,7 @@
 #include "NetworkUtilities.h"
 #include "Portability.h"
 #include "readlink.h"
+#include "../../bionic/libc/dns/include/resolv_netid.h"  // For android_getaddrinfofornet.
 #include "ScopedBytes.h"
 #include "ScopedLocalRef.h"
 #include "ScopedPrimitiveArray.h"
@@ -661,7 +662,8 @@
     return env->NewStringUTF(gai_strerror(error));
 }
 
-static jobjectArray Posix_getaddrinfo(JNIEnv* env, jobject, jstring javaNode, jobject javaHints) {
+static jobjectArray Posix_android_getaddrinfo(JNIEnv* env, jobject, jstring javaNode,
+        jobject javaHints, jint netId) {
     ScopedUtfChars node(env, javaNode);
     if (node.c_str() == NULL) {
         return NULL;
@@ -681,10 +683,10 @@
 
     addrinfo* addressList = NULL;
     errno = 0;
-    int rc = getaddrinfo(node.c_str(), NULL, &hints, &addressList);
+    int rc = android_getaddrinfofornet(node.c_str(), NULL, &hints, netId, 0, &addressList);
     UniquePtr<addrinfo, addrinfo_deleter> addressListDeleter(addressList);
     if (rc != 0) {
-        throwGaiException(env, "getaddrinfo", rc);
+        throwGaiException(env, "android_getaddrinfo", rc);
         return NULL;
     }
 
@@ -694,7 +696,7 @@
         if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
             ++addressCount;
         } else {
-            ALOGE("getaddrinfo unexpected ai_family %i", ai->ai_family);
+            ALOGE("android_getaddrinfo unexpected ai_family %i", ai->ai_family);
         }
     }
     if (addressCount == 0) {
@@ -712,7 +714,7 @@
     for (addrinfo* ai = addressList; ai != NULL; ai = ai->ai_next) {
         if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) {
             // Unknown address family. Skip this address.
-            ALOGE("getaddrinfo unexpected ai_family %i", ai->ai_family);
+            ALOGE("android_getaddrinfo unexpected ai_family %i", ai->ai_family);
             continue;
         }
 
@@ -1537,6 +1539,7 @@
 static JNINativeMethod gMethods[] = {
     NATIVE_METHOD(Posix, accept, "(Ljava/io/FileDescriptor;Ljava/net/InetSocketAddress;)Ljava/io/FileDescriptor;"),
     NATIVE_METHOD(Posix, access, "(Ljava/lang/String;I)Z"),
+    NATIVE_METHOD(Posix, android_getaddrinfo, "(Ljava/lang/String;Landroid/system/StructAddrinfo;I)[Ljava/net/InetAddress;"),
     NATIVE_METHOD(Posix, bind, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"),
     NATIVE_METHOD(Posix, chmod, "(Ljava/lang/String;I)V"),
     NATIVE_METHOD(Posix, chown, "(Ljava/lang/String;II)V"),
@@ -1558,7 +1561,6 @@
     NATIVE_METHOD(Posix, fsync, "(Ljava/io/FileDescriptor;)V"),
     NATIVE_METHOD(Posix, ftruncate, "(Ljava/io/FileDescriptor;J)V"),
     NATIVE_METHOD(Posix, gai_strerror, "(I)Ljava/lang/String;"),
-    NATIVE_METHOD(Posix, getaddrinfo, "(Ljava/lang/String;Landroid/system/StructAddrinfo;)[Ljava/net/InetAddress;"),
     NATIVE_METHOD(Posix, getegid, "()I"),
     NATIVE_METHOD(Posix, geteuid, "()I"),
     NATIVE_METHOD(Posix, getgid, "()I"),
diff --git a/luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java b/luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java
index 6d033d3..1475a63 100644
--- a/luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java
+++ b/luni/src/test/java/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java
@@ -109,6 +109,24 @@
                     "3xQAyMuOHm72exJljYFqIsiNvGE0KufCqCuH1PD97IXMrLlwGmKKg5jP349lySBpJjm6RDqCTT+6" +
                     "dUl2jkVbeNmco99Y7AOdtLsOdXBMCo5x8lK8zwQWFrzEms0joHXCpWfGWA==";
 
+    public static final String ANSSI = "" +
+                    "MIIDbDCCAlSgAwIBAgIDAx2nMA0GCSqGSIb3DQEBBQUAMEsxCzAJBgNVBAYTAkZSMQ4wDAYDVQQK" +
+                    "EwVER1RQRTEsMCoGA1UEAxMjQUMgREdUUEUgU2lnbmF0dXJlIEF1dGhlbnRpZmljYXRpb24wHhcN" +
+                    "MTMwNzE4MTAwNTI4WhcNMTQwNzE4MTAwNTI4WjA+MQswCQYDVQQGEwJGUjETMBEGA1UECgwKREcg" +
+                    "VHLDqXNvcjEaMBgGA1UEAwwRQUMgREcgVHLDqXNvciBTU0wwggEiMA0GCSqGSIb3DQEBAQUAA4IB" +
+                    "DwAwggEKAoIBAQDI0WFSUyY+MmtFkqFjTefoFyDgh9b1C/2YvSIvT8oCH62JWT5rpeTCZwaXbqWc" +
+                    "jaNfzggqaFsokqfhBif43HNHNtNJmvKE32VcuLB0SpsLR/1VeTd9F99C1JeHVa+nelumOHEfouX8" +
+                    "rRFrxNXNIYTVeiENT8Y2YqRb/XAril9g7i674uFzLiNR/t/N/F8Exujv9U8m8rmgud/+tG9WDRaD" +
+                    "Jwoj3ZFCOnL5qLnSUEcS6TzWpozLmC2JVO5GZKGGd7qC9FjdBkVilkbVIEGSrYvz2Uz2v5IGqMBI" +
+                    "QaFL/kSYWxGTaedTOk2drFEApp9AEPTfv1NwCWBfegsGQrHUROM3AgMBAAGjZjBkMBIGA1UdEwEB" +
+                    "/wQIMAYBAf8CAQQwHQYDVR0OBBYEFAAMW8lJqJW0DtAv5p3Mjogxvh9lMB8GA1UdIwQYMBaAFOnb" +
+                    "kI/9W5nkFTvwYlyn5A1Y6IeZMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAtDfG" +
+                    "HkHOLW2d9fiMtwtkEwDauISJLJyCjoRmawzmQbIZXq7HaLliVfE0sdfKUm0iQ0im1/CpnJLPoTeK" +
+                    "yBHvNu1ubLc2m+9dabAYhF3pVdKC+gNaAzBXZ9Gt0p1CLk1lf8Hg+R10HN2IPCv7V/crz2Ga+c23" +
+                    "4P3pfwYW8+Nd7alGCuvqot6UYXOlheF7zWUkHn6z6tvY+9oMDHKSUAthhA/FB50JgJU89zyTv1eg" +
+                    "Y3ldKwvYBW3W3yNZdTHbPyNsPJdhqA55mDNsteE5YTp1PyySDb1MSVrbxDEruoH6ZE99Hob4Ih8A" +
+                    "mn7MHZatGClECgjXWFZ2Gxa7OUCaQpcH8g==";
+
     public CertBlacklistTest() throws IOException {
         tmpFile = File.createTempFile("test", "");
         DEFAULT_PUBKEYS = getDefaultPubkeys();
@@ -415,6 +433,20 @@
         assertEquals(bl.isPublicKeyBlackListed(pk), true);
     }
 
+    public void testANSSISerialBlacklist() throws Exception {
+        CertBlacklist bl = new CertBlacklist();
+        assertEquals(bl.isSerialNumberBlackListed(createSerialNumber(ANSSI)), true);
+    }
+
+    public void testANSSIIntermediatePubkeyBlacklist() 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);
+    }
+
     private static void printHash(String cert) throws Exception {
         System.out.println("CERTIFICATE PUBLIC KEY HASH: " + getHash(createPublicKey(cert)));
     }
diff --git a/luni/src/test/java/libcore/icu/ICUTest.java b/luni/src/test/java/libcore/icu/ICUTest.java
index 3fa1f46..525d372 100644
--- a/luni/src/test/java/libcore/icu/ICUTest.java
+++ b/luni/src/test/java/libcore/icu/ICUTest.java
@@ -198,12 +198,6 @@
     Collator.getInstance(sr_Latn_BA);
     Collator.getInstance(sr_Latn_ME);
 
-    // TODO: This needs to be fixed. We shouldn't output attribute key
-    // expansions in the language tag or the toString output. The tests
-    // will fail with something like:
-    //
-    // expected:<de-u-co[-phonebk-kf-upper-kn]> but was:
-    // <de-u-co[lcasefirst-upper-collation-phonebook-colnumeric-yes]>
     Locale l = Locale.forLanguageTag("de-u-co-phonebk-kf-upper-kn");
     assertEquals("de__#u-co-phonebk-kf-upper-kn", l.toString());
     assertEquals("de-u-co-phonebk-kf-upper-kn", l.toLanguageTag());
diff --git a/luni/src/test/java/libcore/java/lang/DoubleTest.java b/luni/src/test/java/libcore/java/lang/DoubleTest.java
index 687d3e3..85281ba 100644
--- a/luni/src/test/java/libcore/java/lang/DoubleTest.java
+++ b/luni/src/test/java/libcore/java/lang/DoubleTest.java
@@ -127,4 +127,19 @@
         assertEquals(2.2250738585072014E-308, Double.parseDouble("2.22507385850720129978001e-308"));
         assertEquals(-2.2250738585072014E-308, Double.parseDouble("-2.2250738585072012e-308"));
     }
+
+    // https://code.google.com/p/android/issues/detail?id=71216
+    public void testParse_bug71216() {
+        try {
+            Double.parseDouble("73706943-9580-4406-a02f-0304e4324844");
+            fail();
+        } catch (NumberFormatException expected) {
+        }
+
+        try {
+            Double.parseDouble("bade999999999999999999999999999999");
+            fail();
+        } catch (NumberFormatException expected) {
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/java/nio/BufferTest.java b/luni/src/test/java/libcore/java/nio/BufferTest.java
index 613c6fa..c936cdf 100644
--- a/luni/src/test/java/libcore/java/nio/BufferTest.java
+++ b/luni/src/test/java/libcore/java/nio/BufferTest.java
@@ -20,6 +20,8 @@
 import java.io.File;
 import java.io.RandomAccessFile;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.nio.Buffer;
 import java.nio.BufferOverflowException;
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
@@ -600,17 +602,31 @@
         assertTrue(b.isDirect());
         // Check the buffer has an array of the right size.
         assertTrue(b.hasArray());
-        assertEquals(0, b.arrayOffset());
         byte[] array = b.array();
-        assertEquals(10, array.length);
+        assertTrue(array.length >= b.capacity());
+        assertEquals(10, b.capacity());
         // Check that writes to the array show up in the buffer.
         assertEquals(0, b.get(0));
-        array[0] = 1;
+        array[b.arrayOffset()] = 1;
         assertEquals(1, b.get(0));
         // Check that writes to the buffer show up in the array.
-        assertEquals(1, array[0]);
+        assertEquals(1, array[b.arrayOffset()]);
         b.put(0, (byte) 0);
-        assertEquals(0, array[0]);
+        assertEquals(0, array[b.arrayOffset()]);
+    }
+
+    // Test that direct byte buffers are 8 byte aligned.
+    // http://b/16449607
+    public void testDirectByteBufferAlignment() throws Exception {
+        ByteBuffer b = ByteBuffer.allocateDirect(10);
+        Field addressField = Buffer.class.getDeclaredField("effectiveDirectAddress");
+        assertTrue(addressField != null);
+        addressField.setAccessible(true);
+        long address = addressField.getLong(b);
+        // Check that the address field is aligned by 8.
+        // Normally reading this field happens in native code by calling
+        // GetDirectBufferAddress.
+        assertEquals(0, address % 8);
     }
 
     public void testSliceOffset() throws Exception {
@@ -618,14 +634,12 @@
         ByteBuffer buffer = ByteBuffer.allocate(10);
         buffer.get();
         ByteBuffer slice = buffer.slice();
-        assertEquals(0, buffer.arrayOffset());
-        assertEquals(1, slice.arrayOffset());
+        assertEquals(buffer.arrayOffset() + 1, slice.arrayOffset());
 
         ByteBuffer directBuffer = ByteBuffer.allocateDirect(10);
         directBuffer.get();
         ByteBuffer directSlice = directBuffer.slice();
-        assertEquals(0, directBuffer.arrayOffset());
-        assertEquals(1, directSlice.arrayOffset());
+        assertEquals(directBuffer.arrayOffset() + 1, directSlice.arrayOffset());
     }
 
     // http://code.google.com/p/android/issues/detail?id=16184
diff --git a/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java b/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java
index 09a18e2..eb0e3d0 100644
--- a/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java
+++ b/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java
@@ -47,6 +47,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
 import javax.crypto.spec.DHParameterSpec;
 
 import junit.framework.TestCase;
@@ -54,63 +56,28 @@
 public class KeyPairGeneratorTest extends TestCase {
 
     public void test_providerCount() {
-        Provider[] providers = Security.getProviders();
-        // We expect there to be at least one provider.
-        assertTrue(providers.length > 0);
-        // If this fails remember to add _provider methods below. This test is sharded because it
-        // takes a long time to execute.
-        assertTrue(providers.length < 10);
+        // If this fails remember to add/remove _provider methods below. This test is sharded
+        // because it takes so long.
+        assertEquals(4, Security.getProviders().length);
     }
 
     public void test_getInstance_provider0() throws Exception {
-        test_getInstance(0);
+        test_getInstance(Security.getProviders()[0]);
     }
 
     public void test_getInstance_provider1() throws Exception {
-        test_getInstance(1);
+        test_getInstance(Security.getProviders()[1]);
     }
 
     public void test_getInstance_provider2() throws Exception {
-        test_getInstance(2);
+        test_getInstance(Security.getProviders()[2]);
     }
 
     public void test_getInstance_provider3() throws Exception {
-        test_getInstance(3);
+        test_getInstance(Security.getProviders()[3]);
     }
 
-    public void test_getInstance_provider4() throws Exception {
-        test_getInstance(4);
-    }
-
-    public void test_getInstance_provider5() throws Exception {
-        test_getInstance(5);
-    }
-
-    public void test_getInstance_provider6() throws Exception {
-        test_getInstance(6);
-    }
-
-    public void test_getInstance_provider7() throws Exception {
-        test_getInstance(7);
-    }
-
-    public void test_getInstance_provider8() throws Exception {
-        test_getInstance(8);
-    }
-
-    public void test_getInstance_provider9() throws Exception {
-        test_getInstance(9);
-    }
-
-    private void test_getInstance(int providerIndex) throws Exception {
-        Provider[] providers = Security.getProviders();
-        if (providerIndex >= providers.length) {
-            // Providers can be added by vendors and other tests. We do not
-            // specify a fixed number and silenty pass if the provider at the
-            // specified index does not exist.
-            return;
-        }
-        Provider provider = providers[providerIndex];
+    private void test_getInstance(Provider provider) throws Exception {
         Set<Provider.Service> services = provider.getServices();
         for (Provider.Service service : services) {
             String type = service.getType();
@@ -126,12 +93,8 @@
 
             AlgorithmParameterSpec params = null;
 
-            // TODO: detect if we're running in vogar and run the full test
             if ("DH".equals(algorithm)) {
-                // Disabled because this takes too long on devices.
-                // TODO: Re-enable DH test. http://b/5513723.
-                // params = getDHParams();
-                continue;
+                params = getDHParams();
             }
 
             try {
@@ -218,6 +181,14 @@
         test_KeyPair(kpg, kpg.generateKeyPair());
 
         String algorithm = kpg.getAlgorithm();
+
+        // TODO: detect if we're running in vogar and run the full test
+        if ("DH".equals(algorithm)) {
+            // Disabled because this takes too long on devices.
+            // TODO: Re-enable DH test. http://b/5513723.
+            return;
+        }
+
         List<Integer> keySizes = getKeySizes(algorithm);
         for (int keySize : keySizes) {
             kpg.initialize(keySize);
@@ -263,6 +234,17 @@
             expectedAlgorithm = "DH";
         }
         assertEquals(expectedAlgorithm, k.getAlgorithm().toUpperCase());
+        if (expectedAlgorithm.equals("DH")) {
+            if (k instanceof DHPublicKey) {
+                DHPublicKey dhPub = (DHPublicKey) k;
+                assertEquals(dhPub.getParams().getP(), getDHParams().getP());
+            } else if (k instanceof DHPrivateKey) {
+                DHPrivateKey dhPriv = (DHPrivateKey) k;
+                assertEquals(dhPriv.getParams().getP(), getDHParams().getP());
+            } else {
+                fail("not a public or private key!?");
+            }
+        }
         assertNotNull(k.getEncoded());
         assertNotNull(k.getFormat());
 
@@ -344,7 +326,7 @@
      *
      * openssl gendh 512 | openssl dhparams -C
      */
-    private static AlgorithmParameterSpec getDHParams() {
+    private static DHParameterSpec getDHParams() {
         BigInteger p = new BigInteger("E7AB1768BD75CD24700960FFA32D3F1557344E587101237532CC641646ED7A7C104743377F6D46251698B665CE2A6CBAB6714C2569A7D2CA22C0CF03FA40AC93", 16);
         BigInteger g = new BigInteger("02", 16);
         return new DHParameterSpec(p, g, 512);
diff --git a/luni/src/test/java/libcore/java/util/LocaleTest.java b/luni/src/test/java/libcore/java/util/LocaleTest.java
index 94bf363..23a4f28 100644
--- a/luni/src/test/java/libcore/java/util/LocaleTest.java
+++ b/luni/src/test/java/libcore/java/util/LocaleTest.java
@@ -541,13 +541,6 @@
         assertEquals("eng", l.getLanguage());
         assertEquals("419", l.getCountry());
 
-        // IND is an invalid region code so ICU helpfully tries to parse it as
-        // a 3 letter language code, even if it isn't a valid ISO-639-3 code
-        // either.
-        l =  fromLanguageTag("en-USB", useBuilder);
-        assertEquals("usb", l.getLanguage());
-        assertEquals("", l.getCountry());
-
         // Script tags shouldn't be mis-recognized as regions.
         l =  fromLanguageTag("en-Latn", useBuilder);
         assertEquals("en", l.getLanguage());
@@ -612,16 +605,24 @@
         } catch (IllformedLocaleException expected) {
         }
 
-        // Ill-formed extension with long subtag.
+        // Two extension keys in a row (i.e, another case of an ill-formed
+        // empty exception).
         try {
-            fromLanguageTag("en-f-fooobaaaz", true);
+            fromLanguageTag("en-f-g-fo-baar", true);
             fail();
         } catch (IllformedLocaleException expected) {
         }
 
-        // Ill-formed extension key.
+        // Dangling empty key after a well formed extension.
         try {
-            fromLanguageTag("en-9-baa", true);
+            fromLanguageTag("en-f-fo-baar-g", true);
+            fail();
+        } catch (IllformedLocaleException expected) {
+        }
+
+        // Ill-formed extension with long subtag.
+        try {
+            fromLanguageTag("en-f-fooobaaaz", true);
             fail();
         } catch (IllformedLocaleException expected) {
         }
@@ -700,7 +701,7 @@
         assertEquals("en", l.getLanguage());
         assertEquals("Latn", l.getScript());
         assertEquals("GB", l.getCountry());
-        assertEquals("FOOOO_POSIX", l.getVariant());
+        assertEquals("FOOOO", l.getVariant());
         assertEquals("fo-bar-baaz", l.getExtension('g'));
 
         // Multiple extensions
@@ -708,7 +709,7 @@
         assertEquals("en", l.getLanguage());
         assertEquals("Latn", l.getScript());
         assertEquals("US", l.getCountry());
-        assertEquals("FOOOO_POSIX", l.getVariant());
+        assertEquals("FOOOO", l.getVariant());
         assertEquals("fo-bar", l.getExtension('g'));
         assertEquals("go-gaz", l.getExtension('h'));
 
@@ -738,6 +739,13 @@
         assertEquals("", l.getScript());
         assertEquals("", l.getCountry());
         assertEquals("fo", l.getExtension('f'));
+
+        l = fromLanguageTag("en-f-fo-x-a-b-c-d-e-fo", useBuilder);
+        assertEquals("en", l.getLanguage());
+        assertEquals("", l.getScript());
+        assertEquals("", l.getCountry());
+        assertEquals("fo", l.getExtension('f'));
+        assertEquals("a-b-c-d-e-fo", l.getExtension('x'));
     }
 
     public void test_forLanguageTag() {
@@ -782,7 +790,7 @@
 
     public void test_setLanguageTag_malformedTags() {
         Locale l = fromLanguageTag("a", false);
-        assertEquals("", l.getLanguage());
+        assertEquals("und", l.getLanguage());
         assertEquals("", l.getCountry());
         assertEquals("", l.getVariant());
         assertEquals("", l.getScript());
@@ -1113,4 +1121,22 @@
                 .build();
         assertEquals("en-US-POSIX", posix.toLanguageTag());
     }
+
+    public void test_forLanguageTag_grandFatheredLocale() {
+        // Regular grandfathered locale.
+        Locale gaulish = Locale.forLanguageTag("cel-gaulish");
+        assertEquals("xtg", gaulish.getLanguage());
+        assertEquals("cel-gaulish", gaulish.getExtension(Locale.PRIVATE_USE_EXTENSION));
+        assertEquals("", gaulish.getCountry());
+        assertEquals("", gaulish.getScript());
+        assertEquals("", gaulish.getVariant());
+
+        // Irregular grandfathered locale.
+        Locale enochian = Locale.forLanguageTag("i-enochian");
+        assertEquals("und", enochian.getLanguage());
+        assertEquals("i-enochian", enochian.getExtension(Locale.PRIVATE_USE_EXTENSION));
+        assertEquals("", enochian.getCountry());
+        assertEquals("", enochian.getScript());
+        assertEquals("", enochian.getVariant());
+    }
 }
diff --git a/luni/src/test/java/libcore/java/util/OldAndroidLocaleTest.java b/luni/src/test/java/libcore/java/util/OldAndroidLocaleTest.java
index fa761d3..87f2f9d 100644
--- a/luni/src/test/java/libcore/java/util/OldAndroidLocaleTest.java
+++ b/luni/src/test/java/libcore/java/util/OldAndroidLocaleTest.java
@@ -76,9 +76,16 @@
 
     // This one makes sure we have all necessary locales installed.
     public void testICULocales() {
-        String[] locales = new String[] {
-                // List of locales currently required for Android.
-                "en_US", "es_US", "en_GB", "fr_FR", "de_DE", "de_AT", "cs_CZ", "nl_NL" };
+        // List of locales currently required for Android.
+        Locale[] locales = new Locale[] {
+                new Locale("en", "US"),
+                new Locale("es", "US"),
+                new Locale("en", "GB"),
+                new Locale("fr", "FR"),
+                new Locale("de", "DE"),
+                new Locale("de", "AT"),
+                new Locale("cs", "CZ"),
+                new Locale("nl", "NL") };
 
         String[] mondays = new String[] {
                 "Monday", "lunes", "Monday", "lundi", "Montag", "Montag", "pond\u011bl\u00ed", "maandag" };
@@ -87,14 +94,12 @@
                 "USD", "USD", "GBP", "EUR", "EUR", "EUR", "CZK", "EUR"};
 
         for (int i = 0; i < locales.length; i++) {
-            Locale l = new Locale(locales[i].substring(0, 2), locales[i].substring(3));
+            final Locale l = locales[i];
 
-            // Check language part of locale.
             DateFormatSymbols d = new DateFormatSymbols(l);
             assertEquals("Monday name for " + locales[i] + " must match",
                     mondays[i], d.getWeekdays()[2]);
 
-            // Check country part of locale.
             Currency c = Currency.getInstance(l);
             assertEquals("Currency code for " + locales[i] + " must match",
                     currencies[i], c.getCurrencyCode());
diff --git a/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java b/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java
index 0b194f5..e5a6cd8 100644
--- a/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java
+++ b/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java
@@ -122,6 +122,7 @@
             jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE);
             if ("Test.class".equals(zipEntry.getName())) {
                 assertNotNull(jarFile.getCertificates(zipEntry));
+                assertNotNull(jarFile.getCertificateChains(zipEntry));
             }
         }
     }
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index f7a0a72..c89886c 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -1213,11 +1213,19 @@
         if (!isOnlyWrappingAlgorithm(algorithm)) {
             c.init(Cipher.ENCRYPT_MODE, encryptKey, encryptSpec);
             byte[] cipherText = c.doFinal(getActualPlainText(algorithm));
+            byte[] cipherText2 = c.doFinal(getActualPlainText(algorithm));
+            assertEquals(cipherID,
+                         Arrays.toString(cipherText),
+                         Arrays.toString(cipherText2));
             c.init(Cipher.DECRYPT_MODE, getDecryptKey(algorithm), decryptSpec);
             byte[] decryptedPlainText = c.doFinal(cipherText);
             assertEquals(cipherID,
                          Arrays.toString(getExpectedPlainText(algorithm, providerName)),
                          Arrays.toString(decryptedPlainText));
+            byte[] decryptedPlainText2 = c.doFinal(cipherText);
+            assertEquals(cipherID,
+                         Arrays.toString(decryptedPlainText),
+                         Arrays.toString(decryptedPlainText2));
         }
     }
 
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java
index 0e13fb0..dccadbd 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java
@@ -24,7 +24,12 @@
 import java.security.Provider;
 import java.security.Security;
 import java.security.UnrecoverableKeyException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.Callable;
+import libcore.io.IoUtils;
 import libcore.java.security.StandardNames;
 import javax.net.ServerSocketFactory;
 import javax.net.SocketFactory;
@@ -34,12 +39,15 @@
 import javax.net.ssl.ManagerFactoryParameters;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.SSLServerSocketFactory;
 import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 import javax.net.ssl.TrustManagerFactorySpi;
+import javax.net.ssl.X509KeyManager;
 import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
@@ -85,6 +93,94 @@
         }
     }
 
+    public void test_SSLContext_pskOnlyConfiguration_defaultProviderOnly() throws Exception {
+        // Test the scenario where only a PSKKeyManager is provided and no TrustManagers are
+        // provided.
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(
+                new KeyManager[] {
+                        PSKKeyManagerProxy.getConscryptPSKKeyManager(new PSKKeyManagerProxy())
+                },
+                new TrustManager[0],
+                null);
+        List<String> expectedCipherSuites =
+                new ArrayList<String>(StandardNames.CIPHER_SUITES_DEFAULT_PSK);
+        expectedCipherSuites.add(StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION);
+        assertEnabledCipherSuites(expectedCipherSuites, sslContext);
+    }
+
+    public void test_SSLContext_x509AndPskConfiguration_defaultProviderOnly() throws Exception {
+        // Test the scenario where an X509TrustManager and PSKKeyManager are provided.
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(
+                new KeyManager[] {
+                        PSKKeyManagerProxy.getConscryptPSKKeyManager(new PSKKeyManagerProxy())
+                },
+                null, // Use default trust managers, one of which is an X.509 one.
+                null);
+        List<String> expectedCipherSuites =
+                new ArrayList<String>(StandardNames.CIPHER_SUITES_DEFAULT_PSK);
+        expectedCipherSuites.addAll(StandardNames.CIPHER_SUITES_DEFAULT);
+        assertEnabledCipherSuites(expectedCipherSuites, sslContext);
+
+        // Test the scenario where an X509KeyManager and PSKKeyManager are provided.
+        sslContext = SSLContext.getInstance("TLS");
+        // Just an arbitrary X509KeyManager -- it won't be invoked in this test.
+        X509KeyManager x509KeyManager = new RandomPrivateKeyX509ExtendedKeyManager(null);
+        sslContext.init(
+                new KeyManager[] {
+                        x509KeyManager,
+                        PSKKeyManagerProxy.getConscryptPSKKeyManager(new PSKKeyManagerProxy())
+                },
+                new TrustManager[0],
+                null);
+        assertEnabledCipherSuites(expectedCipherSuites, sslContext);
+    }
+
+    public void test_SSLContext_emptyConfiguration_defaultProviderOnly() throws Exception {
+        // Test the scenario where neither X.509 nor PSK KeyManagers or TrustManagers are provided.
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(
+                new KeyManager[0],
+                new TrustManager[0],
+                null);
+        assertEnabledCipherSuites(
+                Arrays.asList(StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION),
+                sslContext);
+    }
+
+    private static void assertEnabledCipherSuites(
+            List<String> expectedCipherSuites, SSLContext sslContext) throws Exception {
+        assertContentsInOrder(
+                expectedCipherSuites, sslContext.createSSLEngine().getEnabledCipherSuites());
+        assertContentsInOrder(
+                expectedCipherSuites,
+                sslContext.createSSLEngine().getSSLParameters().getCipherSuites());
+        assertContentsInOrder(
+                expectedCipherSuites, sslContext.getSocketFactory().getDefaultCipherSuites());
+        assertContentsInOrder(
+                expectedCipherSuites, sslContext.getServerSocketFactory().getDefaultCipherSuites());
+
+        SSLSocket sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket();
+        try {
+            assertContentsInOrder(
+                    expectedCipherSuites, sslSocket.getEnabledCipherSuites());
+            assertContentsInOrder(
+                    expectedCipherSuites, sslSocket.getSSLParameters().getCipherSuites());
+        } finally {
+            IoUtils.closeQuietly(sslSocket);
+        }
+
+        SSLServerSocket sslServerSocket =
+                (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket();
+        try {
+            assertContentsInOrder(
+                    expectedCipherSuites, sslServerSocket.getEnabledCipherSuites());
+        } finally {
+            IoUtils.closeQuietly(sslSocket);
+        }
+    }
+
     public void test_SSLContext_getInstance() throws Exception {
         try {
             SSLContext.getInstance(null);
@@ -463,4 +559,16 @@
         assertTrue(testContext.port != 0);
         testContext.close();
     }
+
+    private static void assertContentsInOrder(List<String> expected, String... actual) {
+        if (expected.size() != actual.length) {
+            fail("Unexpected length. Expected len <" + expected.size()
+                    + ">, actual len <" + actual.length + ">, expected <" + expected
+                    + ">, actual <" + Arrays.asList(actual) + ">");
+        }
+        if (!expected.equals(Arrays.asList(actual))) {
+            fail("Unexpected element(s). Expected <" + expected
+                    + ">, actual <" + Arrays.asList(actual) + ">" );
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java
index 86e96ff..acf69c0 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java
@@ -16,24 +16,199 @@
 
 package libcore.javax.net.ssl;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.SocketException;
+import java.security.KeyManagementException;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.util.Properties;
 import javax.net.ServerSocketFactory;
 import javax.net.SocketFactory;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLContextSpi;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSessionContext;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
 import junit.framework.TestCase;
+import libcore.java.security.StandardNames;
 
 public class SSLSocketFactoryTest extends TestCase {
+    private static final String SSL_PROPERTY = "ssl.SocketFactory.provider";
+
     public void test_SSLSocketFactory_getDefault() {
         SocketFactory sf = SSLSocketFactory.getDefault();
         assertNotNull(sf);
         assertTrue(SSLSocketFactory.class.isAssignableFrom(sf.getClass()));
     }
 
+    public static class FakeSSLSocketProvider extends Provider {
+        public FakeSSLSocketProvider() {
+            super("FakeSSLSocketProvider", 1.0, "Testing provider");
+            put("SSLContext.Default", FakeSSLContextSpi.class.getName());
+        }
+    }
+
+    public static final class FakeSSLContextSpi extends SSLContextSpi {
+        @Override
+        protected void engineInit(KeyManager[] keyManagers, TrustManager[] trustManagers,
+                SecureRandom secureRandom) throws KeyManagementException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        protected SSLSocketFactory engineGetSocketFactory() {
+            return new FakeSSLSocketFactory();
+        }
+
+        @Override
+        protected SSLServerSocketFactory engineGetServerSocketFactory() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        protected SSLEngine engineCreateSSLEngine(String s, int i) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        protected SSLEngine engineCreateSSLEngine() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        protected SSLSessionContext engineGetServerSessionContext() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        protected SSLSessionContext engineGetClientSessionContext() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    public static class FakeSSLSocketFactory extends SSLSocketFactory {
+        public FakeSSLSocketFactory() {
+        }
+
+        @Override
+        public String[] getDefaultCipherSuites() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String[] getSupportedCipherSuites() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Socket createSocket(Socket s, String host, int port, boolean autoClose) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
+                int localPort) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Socket createSocket(InetAddress host, int port) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Socket createSocket(String host, int port, InetAddress localHost, int localPort) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Socket createSocket(String host, int port) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    public void test_SSLSocketFactory_getDefault_cacheInvalidate() throws Exception {
+        String origProvider = resetSslProvider();
+        try {
+            SocketFactory sf1 = SSLSocketFactory.getDefault();
+            assertNotNull(sf1);
+            assertTrue(SSLSocketFactory.class.isAssignableFrom(sf1.getClass()));
+
+            Provider fakeProvider = new FakeSSLSocketProvider();
+            SocketFactory sf4 = null;
+            SSLContext origContext = null;
+            try {
+                origContext = SSLContext.getDefault();
+                Security.insertProviderAt(fakeProvider, 1);
+                SSLContext.setDefault(SSLContext.getInstance("Default", fakeProvider));
+
+                sf4 = SSLSocketFactory.getDefault();
+                assertNotNull(sf4);
+                assertTrue(SSLSocketFactory.class.isAssignableFrom(sf4.getClass()));
+
+                assertFalse(sf1.getClass() + " should not be " + sf4.getClass(),
+                        sf1.getClass().equals(sf4.getClass()));
+            } finally {
+                SSLContext.setDefault(origContext);
+                Security.removeProvider(fakeProvider.getName());
+            }
+
+            SocketFactory sf3 = SSLSocketFactory.getDefault();
+            assertNotNull(sf3);
+            assertTrue(SSLSocketFactory.class.isAssignableFrom(sf3.getClass()));
+
+            assertTrue(sf1.getClass() + " should be " + sf3.getClass(),
+                    sf1.getClass().equals(sf3.getClass()));
+
+            if (!StandardNames.IS_RI) {
+                Security.setProperty(SSL_PROPERTY, FakeSSLSocketFactory.class.getName());
+                SocketFactory sf2 = SSLSocketFactory.getDefault();
+                assertNotNull(sf2);
+                assertTrue(SSLSocketFactory.class.isAssignableFrom(sf2.getClass()));
+
+                assertFalse(sf2.getClass().getName() + " should not be " + Security.getProperty(SSL_PROPERTY),
+                        sf1.getClass().equals(sf2.getClass()));
+                assertTrue(sf2.getClass().equals(sf4.getClass()));
+
+                resetSslProvider();
+            }
+        } finally {
+            Security.setProperty(SSL_PROPERTY, origProvider);
+        }
+    }
+
+    private String resetSslProvider() {
+        String origProvider = Security.getProperty(SSL_PROPERTY);
+
+        try {
+            Field field_secprops = Security.class.getDeclaredField("secprops");
+            field_secprops.setAccessible(true);
+            Properties secprops = (Properties) field_secprops.get(null);
+            secprops.remove(SSL_PROPERTY);
+
+            Class<?> class_services =
+                    Class.forName("org.apache.harmony.security.fortress.Services");
+            Method m_setNeedRefresh = class_services.getMethod("setNeedRefresh");
+            m_setNeedRefresh.invoke(null);
+        } catch (Exception e) {
+            e.printStackTrace();
+            fail("Cannot find a way to clear out the SocketFactory provider");
+        }
+
+        assertNull(Security.getProperty(SSL_PROPERTY));
+        return origProvider;
+    }
+
     public void test_SSLSocketFactory_defaultConfiguration() throws Exception {
         SSLDefaultConfigurationAsserts.assertSSLSocketFactory(
                 (SSLSocketFactory) SSLSocketFactory.getDefault());
diff --git a/luni/src/test/java/libcore/util/ZoneInfoDBTest.java b/luni/src/test/java/libcore/util/ZoneInfoDBTest.java
index 215821d..9875647 100644
--- a/luni/src/test/java/libcore/util/ZoneInfoDBTest.java
+++ b/luni/src/test/java/libcore/util/ZoneInfoDBTest.java
@@ -77,6 +77,29 @@
     }
   }
 
+  // Confirms any caching that exists correctly handles TimeZone mutability.
+  public void testMakeTimeZone_timeZoneMutability() throws Exception {
+    ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT);
+    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 {
+    ZoneInfoDB.TzData data = new ZoneInfoDB.TzData(TZDATA_IN_ROOT);
+    assertNull(data.makeTimeZone("THIS_TZ_DOES_NOT_EXIST"));
+  }
+
   private static String makeCorruptFile() throws Exception {
     return makeTemporaryFile("invalid content".getBytes());
   }
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/SealedObjectTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/SealedObjectTest.java
index 3ea57bf..5cbdab3 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/SealedObjectTest.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/SealedObjectTest.java
@@ -165,6 +165,34 @@
                 + "in cipher.", algorithm, so.getAlgorithm());
     }
 
+    // https://code.google.com/p/android/issues/detail?id=73235
+    public void testGetAlgorithmAfterSerialization() throws Exception {
+        String secret = "secret string";
+        String algorithm = "DES";
+        KeyGenerator kg = KeyGenerator.getInstance(algorithm);
+        Key key = kg.generateKey();
+
+        Cipher cipher = Cipher.getInstance(algorithm);
+        cipher.init(Cipher.ENCRYPT_MODE, key);
+        SealedObject so = new SealedObject(secret, cipher);
+
+        assertEquals("The algorithm name should be the same as used "
+                + "in cipher.", algorithm, so.getAlgorithm());
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(baos);
+        oos.writeObject(so);
+        oos.close();
+
+        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
+        SealedObject readSo = (SealedObject) ois.readObject();
+        ois.close();
+
+        // Bug 73235 would swap the Cipher algorithm and parameters. Parameters is not public but
+        // algorithm is so we check that.
+        assertEquals(so.getAlgorithm(), readSo.getAlgorithm());
+    }
+
     /**
      * getObject(Key key) method testing. Tests if the object sealed with
      * encryption algorithm and specified parameters can be retrieved by
diff --git a/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java
index 719c3b7..a526c2e 100644
--- a/support/src/test/java/libcore/java/security/StandardNames.java
+++ b/support/src/test/java/libcore/java/security/StandardNames.java
@@ -863,6 +863,15 @@
                             "SSL_RSA_WITH_RC4_128_SHA",
                             CIPHER_SUITE_SECURE_RENEGOTIATION);
 
+    // NOTE: This list needs to be kept in sync with Javadoc of javax.net.ssl.SSLSocket and
+    // javax.net.ssl.SSLEngine.
+    public static final List<String> CIPHER_SUITES_DEFAULT_PSK = Arrays.asList(
+            "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
+            "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
+            "TLS_PSK_WITH_AES_128_CBC_SHA",
+            "TLS_PSK_WITH_AES_256_CBC_SHA"
+            );
+
     private static final Set<String> PERMITTED_DEFAULT_KEY_EXCHANGE_ALGS =
             new HashSet<String>(Arrays.asList("RSA",
                                               "DHE_RSA",
diff --git a/support/src/test/java/tests/resources/hyts_signed_ambiguousSignerArray.jar b/support/src/test/java/tests/resources/hyts_signed_ambiguousSignerArray.jar
new file mode 100644
index 0000000..7da4b59
--- /dev/null
+++ b/support/src/test/java/tests/resources/hyts_signed_ambiguousSignerArray.jar
Binary files differ
diff --git a/support/src/test/java/tests/support/Support_MapTest.java b/support/src/test/java/tests/support/Support_MapTest.java
new file mode 100644
index 0000000..5dfc69d
--- /dev/null
+++ b/support/src/test/java/tests/support/Support_MapTest.java
@@ -0,0 +1,191 @@
+/*
+ * 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 tests.support;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import junit.framework.TestCase;
+
+public class Support_MapTest extends TestCase {
+
+    // must be a map containing the string keys "0"-"99" paired with the Integer
+    // values Integer(0) to Integer(99)
+    private final Map<String, Integer> modifiableMap;
+    private final Map<String, Integer> unmodifiableMap;
+
+    public Support_MapTest(String p1, Map<String, Integer> modifiableMap) {
+        super(p1);
+        this.modifiableMap = modifiableMap;
+        unmodifiableMap = Collections.unmodifiableMap(modifiableMap);
+    }
+
+    @Override
+    public void runTest() {
+        testContents(modifiableMap);
+        testContents(unmodifiableMap);
+
+        // values()
+        new Support_UnmodifiableCollectionTest("values() from map test", modifiableMap.values())
+                .runTest();
+        new Support_UnmodifiableCollectionTest("values() from unmodifiable map test",
+                unmodifiableMap.values()).runTest();
+
+        // entrySet()
+        testEntrySet(modifiableMap.entrySet(), unmodifiableMap.entrySet());
+
+        // keySet()
+        testKeySet(modifiableMap.keySet(), unmodifiableMap.keySet());
+    }
+
+    private void testContents(Map<String, Integer> map) {
+        // size
+        assertTrue("Size should return 100, returned: " + map.size(), map.size() == 100);
+
+        // containsKey
+        assertTrue("Should contain the key \"0\"", map.containsKey("0"));
+        assertTrue("Should contain the key \"50\"", map.containsKey("50"));
+        assertTrue("Should not contain the key \"100\"", !map.containsKey("100"));
+
+        // containsValue
+        assertTrue("Should contain the value 0", map.containsValue(0));
+        assertTrue("Should contain the value 50", map.containsValue(50));
+        assertTrue("Should not contain value 100", !map.containsValue(100));
+
+        // get
+        assertTrue("getting \"0\" didn't return 0", map.get("0") == 0);
+        assertTrue("getting \"50\" didn't return 50", map.get("50") == 50);
+        assertNull("getting \"100\" didn't return null", map.get("100"));
+
+        // isEmpty
+        assertTrue("should have returned false to isEmpty", !map.isEmpty());
+    }
+
+    private static void testEntrySet(
+            Set<Map.Entry<String, Integer>> referenceEntrySet,
+            Set<Map.Entry<String, Integer>> entrySet) {
+        // entrySet should be a set of mappings {"0", 0}, {"1",1}... {"99", 99}
+        assertEquals(100, referenceEntrySet.size());
+        assertEquals(100, entrySet.size());
+
+        // The ordering may be undefined for a map implementation but the ordering must be the
+        // same across iterator(), toArray() and toArray(T[]) for a given map *and* the same for the
+        // modifiable and unmodifiable map.
+        crossCheckOrdering(referenceEntrySet, entrySet, Map.Entry.class);
+    }
+
+    private static void testKeySet(Set<String> referenceKeySet, Set<String> keySet) {
+        // keySet should be a set of the strings "0" to "99"
+        testKeySetContents(referenceKeySet);
+        testKeySetContents(keySet);
+
+        // The ordering may be undefined for a map implementation but the ordering must be the
+        // same across iterator(), toArray() and toArray(T[]) for a given map *and* the same for the
+        // modifiable and unmodifiable map.
+        crossCheckOrdering(referenceKeySet, keySet, String.class);
+    }
+
+    private static void testKeySetContents(Set<String> keySet) {
+        // contains
+        assertTrue("should contain \"0\"", keySet.contains("0"));
+        assertTrue("should contain \"50\"", keySet.contains("50"));
+        assertTrue("should not contain \"100\"", !keySet.contains("100"));
+
+        // containsAll
+        HashSet<String> hs = new HashSet<String>();
+        hs.add("0");
+        hs.add("25");
+        hs.add("99");
+        assertTrue("Should contain set of \"0\", \"25\", and \"99\"", keySet.containsAll(hs));
+        hs.add("100");
+        assertTrue("Should not contain set of \"0\", \"25\", \"99\" and \"100\"",
+                !keySet.containsAll(hs));
+
+        // isEmpty
+        assertTrue("Should not be empty", !keySet.isEmpty());
+
+        // size
+        assertEquals("Returned wrong size.", 100, keySet.size());
+    }
+
+    private static <T> void crossCheckOrdering(Set<T> set1, Set<T> set2, Class<?> elementType) {
+        Iterator<T> set1Iterator = set1.iterator();
+        Iterator<T> set2Iterator = set2.iterator();
+
+        T[] zeroLengthArray = createArray(elementType, 0);
+        T[] set1TypedArray1 = set1.toArray(zeroLengthArray);
+        assertEquals(set1.size(), set1TypedArray1.length);
+
+        // Compare set1.iterator(), set2.iterator() and set1.toArray(new T[0])
+        int entryCount = 0;
+        while (set1Iterator.hasNext()) {
+            T set1Entry = set1Iterator.next();
+            T set2Entry = set2Iterator.next();
+
+            // Compare set1 with set2
+            assertEquals(set1Entry, set2Entry);
+
+            // Compare the iterator with the array. The arrays will be checked against each other.
+            assertEquals(set1Entry, set1TypedArray1[entryCount]);
+
+            entryCount++;
+        }
+        assertFalse(set2Iterator.hasNext());
+        assertEquals(set1.size(), entryCount);
+
+        // Compare the various arrays with each other.
+
+        // set1.toArray(new T[size])
+        T[] parameterArray1 = createArray(elementType, set1.size());
+        T[] set1TypedArray2 = set1.toArray(parameterArray1);
+        assertSame(set1TypedArray2, parameterArray1);
+        assertArrayEquals(set1TypedArray1, set1TypedArray2);
+
+        // set1.toArray()
+        Object[] set1UntypedArray = set1.toArray();
+        assertEquals(set1.size(), set1UntypedArray.length);
+        assertArrayEquals(set1TypedArray1, set1UntypedArray);
+
+        // set2.toArray(new T[0])
+        T[] set2TypedArray1 = set2.toArray(zeroLengthArray);
+        assertEquals(set1.size(), set2TypedArray1.length);
+        assertArrayEquals(set1TypedArray1, set2TypedArray1);
+
+        // set2.toArray(new T[size])
+        T[] parameterArray2 = createArray(elementType, set2.size());
+        T[] set2TypedArray2 = set1.toArray(parameterArray2);
+        assertSame(set2TypedArray2, parameterArray2);
+        assertArrayEquals(set1TypedArray1, set1TypedArray2);
+
+        // set2.toArray()
+        Object[] set2UntypedArray = set2.toArray();
+        assertArrayEquals(set1TypedArray1, set2UntypedArray);
+    }
+
+    private static <T> void assertArrayEquals(T[] array1, T[] array2) {
+        assertTrue(Arrays.equals(array1, array2));
+    }
+
+    private static <T> T[] createArray(Class<?> elementType, int size) {
+        return (T[]) Array.newInstance(elementType, size);
+    }
+}
diff --git a/support/src/test/java/tests/support/Support_UnmodifiableMapTest.java b/support/src/test/java/tests/support/Support_UnmodifiableMapTest.java
deleted file mode 100644
index e7a1620..0000000
--- a/support/src/test/java/tests/support/Support_UnmodifiableMapTest.java
+++ /dev/null
@@ -1,161 +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 tests.support;
-
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import junit.framework.TestCase;
-
-public class Support_UnmodifiableMapTest extends TestCase {
-
-    Map<String, Integer> map;
-
-    // must be a map containing the string keys "0"-"99" paired with the Integer
-    // values Integer(0) to Integer(99)
-
-    public Support_UnmodifiableMapTest(String p1) {
-        super(p1);
-    }
-
-    public Support_UnmodifiableMapTest(String p1, Map<String, Integer> m) {
-        super(p1);
-        map = m;
-    }
-
-    @Override
-    public void runTest() {
-        // containsKey
-        assertTrue("UnmodifiableMapTest - Should contain the key \"0\"", map
-                .containsKey("0"));
-        assertTrue("UnmodifiableMapTest - Should contain the key \"50\"", map
-                .containsKey("50"));
-        assertTrue("UnmodifiableMapTest - Should not contain the key \"100\"",
-                !map.containsKey("100"));
-
-        // containsValue
-        assertTrue("UnmodifiableMapTest - Should contain the value 0", map
-                .containsValue(new Integer(0)));
-        assertTrue("UnmodifiableMapTest - Should contain the value 50", map
-                .containsValue(new Integer(50)));
-        assertTrue("UnmodifiableMapTest - Should not contain value 100", !map
-                .containsValue(new Integer(100)));
-
-        // entrySet
-        Set<?> entrySet = map.entrySet();
-        Iterator<?> entrySetIterator = entrySet.iterator();
-        int myCounter = 0;
-        while (entrySetIterator.hasNext()) {
-            Map.Entry<?, ?> me = (Map.Entry<?, ?>) entrySetIterator.next();
-            assertTrue("UnmodifiableMapTest - Incorrect Map.Entry returned",
-                    map.get(me.getKey()).equals(me.getValue()));
-            myCounter++;
-        }
-        assertEquals("UnmodifiableMapTest - Incorrect number of map entries returned",
-                100, myCounter);
-
-        // get
-        assertTrue("UnmodifiableMapTest - getting \"0\" didn't return 0",
-                map.get("0").intValue() == 0);
-        assertTrue("UnmodifiableMapTest - getting \"50\" didn't return 0",
-                map.get("0").intValue() == 0);
-        assertNull("UnmodifiableMapTest - getting \"100\" didn't return null",
-                map.get("100"));
-
-        // isEmpty
-        assertTrue(
-                "UnmodifiableMapTest - should have returned false to isEmpty",
-                !map.isEmpty());
-
-        // keySet
-        Set<?> keySet = map.keySet();
-        t_KeySet(keySet);
-
-        // size
-        assertTrue("Size should return 100, returned: " + map.size(), map
-                .size() == 100);
-
-        // values
-        new Support_UnmodifiableCollectionTest("Unmod--from map test", map
-                .values());
-
-    }
-
-    void t_KeySet(Set<?> keySet) {
-        // keySet should be a set of the strings "0" to "99"
-
-        // contains
-        assertTrue("UnmodifiableMapTest - keySetTest - should contain \"0\"",
-                keySet.contains("0"));
-        assertTrue("UnmodifiableMapTest - keySetTest - should contain \"50\"",
-                keySet.contains("50"));
-        assertTrue(
-                "UnmodifiableMapTest - keySetTest - should not contain \"100\"",
-                !keySet.contains("100"));
-
-        // containsAll
-        HashSet<String> hs = new HashSet<String>();
-        hs.add("0");
-        hs.add("25");
-        hs.add("99");
-        assertTrue(
-                "UnmodifiableMapTest - keySetTest - should contain set of \"0\", \"25\", and \"99\"",
-                keySet.containsAll(hs));
-        hs.add("100");
-        assertTrue(
-                "UnmodifiableMapTest - keySetTest - should not contain set of \"0\", \"25\", \"99\" and \"100\"",
-                !keySet.containsAll(hs));
-
-        // isEmpty
-        assertTrue("UnmodifiableMapTest - keySetTest - should not be empty",
-                !keySet.isEmpty());
-
-        // iterator
-        Iterator<?> it = keySet.iterator();
-        while (it.hasNext()) {
-            assertTrue(
-                    "UnmodifiableMapTest - keySetTest - Iterator returned wrong values",
-                    keySet.contains(it.next()));
-        }
-
-        // size
-        assertTrue(
-                "UnmodifiableMapTest - keySetTest - returned wrong size.  Wanted 100, got: "
-                        + keySet.size(), keySet.size() == 100);
-
-        // toArray
-        Object[] objArray;
-        objArray = keySet.toArray();
-        for (int counter = 0; it.hasNext(); counter++) {
-            assertTrue(
-                    "UnmodifiableMapTest - keySetTest - toArray returned incorrect array",
-                    objArray[counter] == it.next());
-        }
-
-        // toArray (Object[])
-        objArray = new Object[100];
-        keySet.toArray(objArray);
-        for (int counter = 0; it.hasNext(); counter++) {
-            assertTrue(
-                    "UnmodifiableMapTest - keySetTest - toArray(Object) filled array incorrectly",
-                    objArray[counter] == it.next());
-        }
-    }
-
-}