merge in klp-release history after reset to klp-dev
diff --git a/benchmarks/src/benchmarks/regression/DateIntervalFormatBenchmark.java b/benchmarks/src/benchmarks/regression/DateIntervalFormatBenchmark.java
new file mode 100644
index 0000000..02d8f97
--- /dev/null
+++ b/benchmarks/src/benchmarks/regression/DateIntervalFormatBenchmark.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 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.SimpleBenchmark;
+
+import java.util.Locale;
+import java.util.TimeZone;
+
+import static libcore.icu.DateIntervalFormat.*;
+
+public class DateIntervalFormatBenchmark extends SimpleBenchmark {
+  public void timeDateIntervalFormat_formatDateRange_DATE(int reps) throws Exception {
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("UTC");
+    int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY;
+
+    for (int rep = 0; rep < reps; ++rep) {
+      formatDateRange(l, utc, 0L, 0L, flags);
+    }
+  }
+
+  public void timeDateIntervalFormat_formatDateRange_TIME(int reps) throws Exception {
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("UTC");
+    int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR;
+
+    for (int rep = 0; rep < reps; ++rep) {
+      formatDateRange(l, utc, 0L, 0L, flags);
+    }
+  }
+
+  public void timeDateIntervalFormat_formatDateRange_DATE_TIME(int reps) throws Exception {
+    Locale l = Locale.US;
+    TimeZone utc = TimeZone.getTimeZone("UTC");
+    int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_TIME | FORMAT_24HOUR;
+
+    for (int rep = 0; rep < reps; ++rep) {
+      formatDateRange(l, utc, 0L, 0L, flags);
+    }
+  }
+}
diff --git a/crypto/src/main/java/org/conscrypt/OpenSSLCipher.java b/crypto/src/main/java/org/conscrypt/OpenSSLCipher.java
index 632f9e2..7acccc7 100644
--- a/crypto/src/main/java/org/conscrypt/OpenSSLCipher.java
+++ b/crypto/src/main/java/org/conscrypt/OpenSSLCipher.java
@@ -16,6 +16,7 @@
 
 package org.conscrypt;
 
+import java.io.IOException;
 import java.security.AlgorithmParameters;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
@@ -118,6 +119,11 @@
     }
 
     /**
+     * Returns the standard name for the particular algorithm.
+     */
+    protected abstract String getBaseCipherName();
+
+    /**
      * Returns the OpenSSL cipher name for the particular {@code keySize} and
      * cipher {@code mode}.
      */
@@ -214,10 +220,22 @@
 
     @Override
     protected AlgorithmParameters engineGetParameters() {
+        if (iv != null && iv.length > 0) {
+            try {
+                AlgorithmParameters params = AlgorithmParameters.getInstance(getBaseCipherName());
+                params.init(iv);
+                return params;
+            } catch (NoSuchAlgorithmException e) {
+                return null;
+            } catch (IOException e) {
+                return null;
+            }
+        }
         return null;
     }
 
-    private void engineInitInternal(int opmode, Key key, byte[] iv) throws InvalidKeyException, InvalidAlgorithmParameterException {
+    private void engineInitInternal(int opmode, Key key, byte[] iv, SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
         if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) {
             encrypting = true;
         } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) {
@@ -245,9 +263,15 @@
         }
 
         final int ivLength = NativeCrypto.EVP_CIPHER_iv_length(cipherType);
-        if (iv == null) {
+        if (iv == null && ivLength != 0) {
             iv = new byte[ivLength];
-        } else if (iv.length != ivLength) {
+            if (encrypting) {
+                if (random == null) {
+                    random = new SecureRandom();
+                }
+                random.nextBytes(iv);
+            }
+        } else if (iv != null && iv.length != ivLength) {
             throw new InvalidAlgorithmParameterException("expected IV length of " + ivLength);
         }
 
@@ -273,7 +297,7 @@
     @Override
     protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
         try {
-            engineInitInternal(opmode, key, null);
+            engineInitInternal(opmode, key, null, random);
         } catch (InvalidAlgorithmParameterException e) {
             throw new RuntimeException(e);
         }
@@ -290,7 +314,7 @@
             iv = null;
         }
 
-        engineInitInternal(opmode, key, iv);
+        engineInitInternal(opmode, key, iv, random);
     }
 
     @Override
@@ -631,6 +655,11 @@
         }
 
         @Override
+        protected String getBaseCipherName() {
+            return "AES";
+        }
+
+        @Override
         protected String getCipherName(int keyLength, Mode mode) {
             return "aes-" + (keyLength * 8) + "-" + mode.toString().toLowerCase(Locale.US);
         }
@@ -721,6 +750,11 @@
         }
 
         @Override
+        protected String getBaseCipherName() {
+            return "DESede";
+        }
+
+        @Override
         protected String getCipherName(int keySize, Mode mode) {
             final String baseCipherName;
             if (keySize == 16) {
@@ -780,6 +814,11 @@
         }
 
         @Override
+        protected String getBaseCipherName() {
+            return "ARCFOUR";
+        }
+
+        @Override
         protected String getCipherName(int keySize, Mode mode) {
             return "rc4";
         }
diff --git a/crypto/src/main/native/org_conscrypt_NativeCrypto.cpp b/crypto/src/main/native/org_conscrypt_NativeCrypto.cpp
index 33be429..f493e5a 100644
--- a/crypto/src/main/native/org_conscrypt_NativeCrypto.cpp
+++ b/crypto/src/main/native/org_conscrypt_NativeCrypto.cpp
@@ -857,6 +857,14 @@
 }
 
 /**
+ * To avoid the round-trip to ASN.1 and back in X509_dup, we just up the reference count.
+ */
+static X509* X509_dup_nocopy(X509* x509) {
+    CRYPTO_add(&x509->references, 1, CRYPTO_LOCK_X509);
+    return x509;
+}
+
+/**
  * BIO for InputStream
  */
 class BIO_Stream {
@@ -4963,7 +4971,7 @@
     ScopedLocalRef<jlongArray> certArray(env, env->NewLongArray(size));
     ScopedLongArrayRW certs(env, certArray.get());
     for (size_t i = 0; i < size; i++) {
-        X509* item = reinterpret_cast<X509*>(sk_X509_value(path.get(), i));
+        X509* item = reinterpret_cast<X509*>(sk_X509_shift(path.get()));
         certs[i] = reinterpret_cast<uintptr_t>(item);
     }
 
@@ -4987,7 +4995,7 @@
 
     for (size_t i = 0; i < certsArray.size(); i++) {
         X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(certsArray[i]));
-        sk_X509_push(certStack.get(), X509_dup(x509));
+        sk_X509_push(certStack.get(), X509_dup_nocopy(x509));
     }
 
     int len;
@@ -7234,14 +7242,14 @@
         JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => threw exception", ssl);
         return NULL;
     }
-    if (!sk_X509_push(chain.get(), certificate)) {
+    if (!sk_X509_push(chain.get(), X509_dup_nocopy(certificate))) {
         jniThrowOutOfMemory(env, "Unable to push local certificate");
         JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => NULL", ssl);
         return NULL;
     }
     STACK_OF(X509)* cert_chain = SSL_get_certificate_chain(ssl, certificate);
     for (int i=0; i<sk_X509_num(cert_chain); i++) {
-        if (!sk_X509_push(chain.get(), sk_X509_value(cert_chain, i))) {
+        if (!sk_X509_push(chain.get(), X509_dup_nocopy(sk_X509_value(cert_chain, i)))) {
             jniThrowOutOfMemory(env, "Unable to push local certificate chain");
             JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => NULL", ssl);
             return NULL;
@@ -7269,13 +7277,21 @@
             JNI_TRACE("ssl=%p NativeCrypto_SSL_get_peer_cert_chain => NULL", ssl);
             return NULL;
         }
-        chain_copy.reset(sk_X509_dup(chain));
+        chain_copy.reset(sk_X509_new_null());
         if (chain_copy.get() == NULL) {
             jniThrowOutOfMemory(env, "Unable to allocate peer certificate chain");
             JNI_TRACE("ssl=%p NativeCrypto_SSL_get_peer_cert_chain => certificate dup error", ssl);
             return NULL;
         }
-        if (!sk_X509_push(chain_copy.get(), x509)) {
+        size_t chain_size = sk_X509_num(chain);
+        for (size_t i = 0; i < chain_size; i++) {
+            if (!sk_X509_push(chain_copy.get(), X509_dup_nocopy(sk_X509_value(chain, i)))) {
+                jniThrowOutOfMemory(env, "Unable to push server's peer certificate chain");
+                JNI_TRACE("ssl=%p NativeCrypto_SSL_get_peer_cert_chain => certificate chain push error", ssl);
+                return NULL;
+            }
+        }
+        if (!sk_X509_push(chain_copy.get(), X509_dup_nocopy(x509))) {
             jniThrowOutOfMemory(env, "Unable to push server's peer certificate");
             JNI_TRACE("ssl=%p NativeCrypto_SSL_get_peer_cert_chain => certificate push error", ssl);
             return NULL;
diff --git a/luni/src/main/java/java/util/zip/ZipEntry.java b/luni/src/main/java/java/util/zip/ZipEntry.java
index 76bbe26..f64c717 100644
--- a/luni/src/main/java/java/util/zip/ZipEntry.java
+++ b/luni/src/main/java/java/util/zip/ZipEntry.java
@@ -343,19 +343,21 @@
 
     /*
      * Internal constructor.  Creates a new ZipEntry by reading the
-     * Central Directory Entry from "in", which must be positioned at
-     * the CDE signature.
+     * Central Directory Entry (CDE) from "in", which must be positioned
+     * at the CDE signature.
      *
-     * On exit, "in" will be positioned at the start of the next entry.
+     * On exit, "in" will be positioned at the start of the next entry
+     * in the Central Directory.
      */
-    ZipEntry(byte[] hdrBuf, InputStream in) throws IOException {
-        Streams.readFully(in, hdrBuf, 0, hdrBuf.length);
+    ZipEntry(byte[] cdeHdrBuf, InputStream cdStream) throws IOException {
+        Streams.readFully(cdStream, cdeHdrBuf, 0, cdeHdrBuf.length);
 
-        BufferIterator it = HeapBufferIterator.iterator(hdrBuf, 0, hdrBuf.length, ByteOrder.LITTLE_ENDIAN);
+        BufferIterator it = HeapBufferIterator.iterator(cdeHdrBuf, 0, cdeHdrBuf.length,
+                ByteOrder.LITTLE_ENDIAN);
 
         int sig = it.readInt();
         if (sig != CENSIG) {
-             throw new ZipException("Central Directory Entry not found");
+            ZipFile.throwZipException("Central Directory Entry", sig);
         }
 
         it.seek(8);
@@ -383,7 +385,7 @@
         localHeaderRelOffset = ((long) it.readInt()) & 0xffffffffL;
 
         byte[] nameBytes = new byte[nameLength];
-        Streams.readFully(in, nameBytes, 0, nameBytes.length);
+        Streams.readFully(cdStream, nameBytes, 0, nameBytes.length);
         if (containsNulByte(nameBytes)) {
             throw new ZipException("Filename contains NUL byte: " + Arrays.toString(nameBytes));
         }
@@ -391,14 +393,14 @@
 
         if (extraLength > 0) {
             extra = new byte[extraLength];
-            Streams.readFully(in, extra, 0, extraLength);
+            Streams.readFully(cdStream, extra, 0, extraLength);
         }
 
         // The RI has always assumed UTF-8. (If GPBF_UTF8_FLAG isn't set, the encoding is
         // actually IBM-437.)
         if (commentByteCount > 0) {
             byte[] commentBytes = new byte[commentByteCount];
-            Streams.readFully(in, commentBytes, 0, commentByteCount);
+            Streams.readFully(cdStream, commentBytes, 0, commentByteCount);
             comment = new String(commentBytes, 0, commentBytes.length, StandardCharsets.UTF_8);
         }
     }
diff --git a/luni/src/main/java/java/util/zip/ZipFile.java b/luni/src/main/java/java/util/zip/ZipFile.java
index 8f5b37e..620572d 100644
--- a/luni/src/main/java/java/util/zip/ZipFile.java
+++ b/luni/src/main/java/java/util/zip/ZipFile.java
@@ -20,7 +20,6 @@
 import dalvik.system.CloseGuard;
 import java.io.BufferedInputStream;
 import java.io.Closeable;
-import java.io.EOFException;
 import java.io.DataInputStream;
 import java.io.File;
 import java.io.IOException;
@@ -40,7 +39,7 @@
  * the zip file's central directory up front (from the constructor), but if you're using
  * {@link #getEntry} to look up multiple files by name, you get the benefit of this index.
  *
- * <p>If you only want to iterate through all the files (using {@link #entries}, you should
+ * <p>If you only want to iterate through all the files (using {@link #entries()}, you should
  * consider {@link ZipInputStream}, which provides stream-like read access to a zip file and
  * has a lower up-front cost because you don't pay to build an in-memory index.
  *
@@ -274,11 +273,19 @@
         RandomAccessFile localRaf = raf;
         synchronized (localRaf) {
             // We don't know the entry data's start position. All we have is the
-            // position of the entry's local header. At position 6 we find the
-            // General Purpose Bit Flag.
+            // position of the entry's local header.
             // http://www.pkware.com/documents/casestudies/APPNOTE.TXT
-            RAFStream rafStream= new RAFStream(localRaf, entry.localHeaderRelOffset + 6);
+            RAFStream rafStream = new RAFStream(localRaf, entry.localHeaderRelOffset);
             DataInputStream is = new DataInputStream(rafStream);
+
+            final int localMagic = Integer.reverseBytes(is.readInt());
+            if (localMagic != LOCSIG) {
+                throwZipException("Local File Header", localMagic);
+            }
+
+            is.skipBytes(2);
+
+            // At position 6 we find the General Purpose Bit Flag.
             int gpbf = Short.reverseBytes(is.readShort()) & 0xffff;
             if ((gpbf & ZipFile.GPBF_UNSUPPORTED_MASK) != 0) {
                 throw new ZipException("Invalid General Purpose Bit Flag: " + gpbf);
@@ -294,13 +301,13 @@
             // Skip the variable-size file name and extra field data.
             rafStream.skip(fileNameLength + extraFieldLength);
 
-            if (entry.compressionMethod == ZipEntry.DEFLATED) {
-                rafStream.length = rafStream.offset + entry.compressedSize;
+            if (entry.compressionMethod == ZipEntry.STORED) {
+                rafStream.endOffset = rafStream.offset + entry.size;
+                return rafStream;
+            } else {
+                rafStream.endOffset = rafStream.offset + entry.compressedSize;
                 int bufSize = Math.max(1024, (int) Math.min(entry.getSize(), 65535L));
                 return new ZipInflaterInputStream(rafStream, new Inflater(true), bufSize, entry);
-            } else {
-                rafStream.length = rafStream.offset + entry.size;
-                return rafStream;
             }
         }
     }
@@ -348,21 +355,26 @@
             throw new ZipException("File too short to be a zip file: " + raf.length());
         }
 
+        raf.seek(0);
+        final int headerMagic = Integer.reverseBytes(raf.readInt());
+        if (headerMagic != LOCSIG) {
+            throw new ZipException("Not a zip archive");
+        }
+
         long stopOffset = scanOffset - 65536;
         if (stopOffset < 0) {
             stopOffset = 0;
         }
 
-        final int ENDHEADERMAGIC = 0x06054b50;
         while (true) {
             raf.seek(scanOffset);
-            if (Integer.reverseBytes(raf.readInt()) == ENDHEADERMAGIC) {
+            if (Integer.reverseBytes(raf.readInt()) == ENDSIG) {
                 break;
             }
 
             scanOffset--;
             if (scanOffset < stopOffset) {
-                throw new ZipException("EOCD not found; not a zip file?");
+                throw new ZipException("End Of Central Directory signature not found");
             }
         }
 
@@ -382,7 +394,7 @@
         int commentLength = it.readShort() & 0xffff;
 
         if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) {
-            throw new ZipException("spanned archives not supported");
+            throw new ZipException("Spanned archives not supported");
         }
 
         if (commentLength > 0) {
@@ -400,6 +412,9 @@
         byte[] hdrBuf = new byte[CENHDR]; // Reuse the same buffer for each entry.
         for (int i = 0; i < numEntries; ++i) {
             ZipEntry newEntry = new ZipEntry(hdrBuf, bufferedStream);
+            if (newEntry.localHeaderRelOffset >= centralDirOffset) {
+                throw new ZipException("Local file header offset is after central directory");
+            }
             String entryName = newEntry.getName();
             if (entries.put(entryName, newEntry) != null) {
                 throw new ZipException("Duplicate entry name: " + entryName);
@@ -407,6 +422,11 @@
         }
     }
 
+    static void throwZipException(String msg, int magic) throws ZipException {
+        final String hexString = IntegralToString.intToHexString(magic, true, 8);
+        throw new ZipException(msg + " signature not found; was " + hexString);
+    }
+
     /**
      * Wrap a stream around a RandomAccessFile.  The RandomAccessFile is shared
      * among all streams returned by getInputStream(), so we have to synchronize
@@ -417,17 +437,17 @@
      */
     static class RAFStream extends InputStream {
         private final RandomAccessFile sharedRaf;
-        private long length;
+        private long endOffset;
         private long offset;
 
         public RAFStream(RandomAccessFile raf, long initialOffset) throws IOException {
             sharedRaf = raf;
             offset = initialOffset;
-            length = raf.length();
+            endOffset = raf.length();
         }
 
         @Override public int available() throws IOException {
-            return (offset < length ? 1 : 0);
+            return (offset < endOffset ? 1 : 0);
         }
 
         @Override public int read() throws IOException {
@@ -436,10 +456,15 @@
 
         @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
             synchronized (sharedRaf) {
-                sharedRaf.seek(offset);
-                if (byteCount > length - offset) {
-                    byteCount = (int) (length - offset);
+                final long length = endOffset - offset;
+                if (byteOffset > length) {
+                    throw new IOException("Byte offset is past end of stream: " + byteOffset
+                            + " > " + length);
                 }
+                if (byteCount > length - byteOffset) {
+                    byteCount = (int) length - byteOffset;
+                }
+                sharedRaf.seek(offset);
                 int count = sharedRaf.read(buffer, byteOffset, byteCount);
                 if (count > 0) {
                     offset += count;
@@ -451,8 +476,8 @@
         }
 
         @Override public long skip(long byteCount) throws IOException {
-            if (byteCount > length - offset) {
-                byteCount = length - offset;
+            if (byteCount > endOffset - offset) {
+                byteCount = endOffset - offset;
             }
             offset += byteCount;
             return byteCount;
@@ -460,7 +485,7 @@
 
         public int fill(Inflater inflater, int nativeEndBufSize) throws IOException {
             synchronized (sharedRaf) {
-                int len = Math.min((int) (length - offset), nativeEndBufSize);
+                int len = Math.min((int) (endOffset - offset), nativeEndBufSize);
                 int cnt = inflater.setFileInput(sharedRaf.getFD(), offset, nativeEndBufSize);
                 // setFileInput read from the file, so we need to get the OS and RAFStream back
                 // in sync...
@@ -480,8 +505,19 @@
         }
 
         @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
-            int i = super.read(buffer, byteOffset, byteCount);
-            if (i != -1) {
+            final int i;
+            try {
+                i = super.read(buffer, byteOffset, byteCount);
+            } catch (IOException e) {
+                throw new IOException("Error reading data for " + entry.getName() + " near offset "
+                        + bytesRead, e);
+            }
+            if (i == -1) {
+                if (entry.size != bytesRead) {
+                    throw new IOException("Size mismatch on inflated file: " + bytesRead + " vs "
+                            + entry.size);
+                }
+            } else {
                 bytesRead += i;
             }
             return i;
diff --git a/luni/src/main/java/libcore/icu/DateIntervalFormat.java b/luni/src/main/java/libcore/icu/DateIntervalFormat.java
index a71989e..ab9085f 100644
--- a/luni/src/main/java/libcore/icu/DateIntervalFormat.java
+++ b/luni/src/main/java/libcore/icu/DateIntervalFormat.java
@@ -19,6 +19,7 @@
 import java.util.Calendar;
 import java.util.Locale;
 import java.util.TimeZone;
+import libcore.util.BasicLruCache;
 
 /**
  * Exposes icu4c's DateIntervalFormat.
@@ -46,7 +47,18 @@
   private static final int DAY_IN_MS = 24 * 60 * 60 * 1000;
   private static final int EPOCH_JULIAN_DAY = 2440588;
 
-  // TODO: check whether icu4c's DateIntervalFormat is expensive enough to warrant a native peer.
+  private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();
+
+  static class FormatterCache extends BasicLruCache<String, Long> {
+    FormatterCache() {
+      super(8);
+    }
+
+    protected void entryEvicted(String key, Long value) {
+      destroyDateIntervalFormat(value);
+    }
+  };
+
   private DateIntervalFormat() {
   }
 
@@ -86,7 +98,20 @@
     }
 
     String skeleton = toSkeleton(startCalendar, endCalendar, flags);
-    return formatDateInterval(skeleton, locale.toString(), tz.getID(), startMs, endMs);
+    synchronized (CACHED_FORMATTERS) {
+      return formatDateInterval(getFormatter(skeleton, locale.toString(), tz.getID()), startMs, endMs);
+    }
+  }
+
+  private static long getFormatter(String skeleton, String localeName, String tzName) {
+    String key = skeleton + "\t" + localeName + "\t" + tzName;
+    Long formatter = CACHED_FORMATTERS.get(key);
+    if (formatter != null) {
+      return formatter;
+    }
+    long address = createDateIntervalFormat(skeleton, localeName, tzName);
+    CACHED_FORMATTERS.put(key, address);
+    return address;
   }
 
   private static String toSkeleton(Calendar startCalendar, Calendar endCalendar, int flags) {
@@ -204,5 +229,7 @@
     return (int) (utcMs / DAY_IN_MS) + EPOCH_JULIAN_DAY;
   }
 
-  private static native String formatDateInterval(String skeleton, String localeName, String timeZoneName, long fromDate, long toDate);
+  private static native long createDateIntervalFormat(String skeleton, String localeName, String tzName);
+  private static native void destroyDateIntervalFormat(long address);
+  private static native String formatDateInterval(long address, long fromDate, long toDate);
 }
diff --git a/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp b/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp
index 65ace4e..72bc631 100644
--- a/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp
+++ b/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp
@@ -23,41 +23,52 @@
 #include "cutils/log.h"
 #include "unicode/dtitvfmt.h"
 
-static jstring DateIntervalFormat_formatDateInterval(JNIEnv* env, jclass, jstring javaSkeleton, jstring javaLocaleName, jstring javaTzName, jlong fromDate, jlong toDate) {
+static jlong DateIntervalFormat_createDateIntervalFormat(JNIEnv* env, jclass, jstring javaSkeleton, jstring javaLocaleName, jstring javaTzName) {
   Locale locale = getLocale(env, javaLocaleName);
 
   ScopedJavaUnicodeString skeletonHolder(env, javaSkeleton);
   if (!skeletonHolder.valid()) {
-    return NULL;
+    return 0;
+  }
+
+  UErrorCode status = U_ZERO_ERROR;
+  DateIntervalFormat* formatter(DateIntervalFormat::createInstance(skeletonHolder.unicodeString(), locale, status));
+  if (maybeThrowIcuException(env, "DateIntervalFormat::createInstance", status)) {
+    return 0;
   }
 
   ScopedJavaUnicodeString tzNameHolder(env, javaTzName);
   if (!tzNameHolder.valid()) {
-    return NULL;
+    return 0;
   }
-
-  UErrorCode status = U_ZERO_ERROR;
-  UniquePtr<DateIntervalFormat> formatter(DateIntervalFormat::createInstance(skeletonHolder.unicodeString(), locale, status));
-  if (maybeThrowIcuException(env, "DateIntervalFormat::createInstance", status)) {
-    return NULL;
-  }
-
   formatter->adoptTimeZone(TimeZone::createTimeZone(tzNameHolder.unicodeString()));
 
+  return reinterpret_cast<uintptr_t>(formatter);
+}
+
+static void DateIntervalFormat_destroyDateIntervalFormat(JNIEnv*, jclass, jlong address) {
+  delete reinterpret_cast<DateIntervalFormat*>(address);
+}
+
+static jstring DateIntervalFormat_formatDateInterval(JNIEnv* env, jclass, jlong address, jlong fromDate, jlong toDate) {
+  DateIntervalFormat* formatter(reinterpret_cast<DateIntervalFormat*>(address));
   DateInterval date_interval(fromDate, toDate);
 
-  UnicodeString result;
+  UnicodeString s;
   FieldPosition pos = 0;
-  formatter->format(&date_interval, result, pos, status);
+  UErrorCode status = U_ZERO_ERROR;
+  formatter->format(&date_interval, s, pos, status);
   if (maybeThrowIcuException(env, "DateIntervalFormat::format", status)) {
-      return NULL;
+    return NULL;
   }
 
-  return env->NewString(result.getBuffer(), result.length());
+  return env->NewString(s.getBuffer(), s.length());
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(DateIntervalFormat, formatDateInterval, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JJ)Ljava/lang/String;"),
+  NATIVE_METHOD(DateIntervalFormat, createDateIntervalFormat, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J"),
+  NATIVE_METHOD(DateIntervalFormat, destroyDateIntervalFormat, "(J)V"),
+  NATIVE_METHOD(DateIntervalFormat, formatDateInterval, "(JJJ)Ljava/lang/String;"),
 };
 void register_libcore_icu_DateIntervalFormat(JNIEnv* env) {
   jniRegisterNativeMethods(env, "libcore/icu/DateIntervalFormat", gMethods, NELEM(gMethods));
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index dac43a2..7922a04 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -21,6 +21,7 @@
 import java.io.PrintStream;
 import java.math.BigInteger;
 import java.nio.ByteBuffer;
+import java.security.AlgorithmParameters;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.Key;
@@ -778,8 +779,10 @@
 
         // Cipher.getInstance(String)
         Cipher c1 = Cipher.getInstance(algorithm);
-        assertEquals(algorithm, c1.getAlgorithm());
-        test_Cipher(c1);
+        if (provider.equals(c1.getProvider())) {
+            assertEquals(algorithm, c1.getAlgorithm());
+            test_Cipher(c1);
+        }
 
         // Cipher.getInstance(String, Provider)
         Cipher c2 = Cipher.getInstance(algorithm, provider);
@@ -812,6 +815,23 @@
 
         final AlgorithmParameterSpec encryptSpec = getEncryptAlgorithmParameterSpec(algorithm);
         int encryptMode = getEncryptMode(algorithm);
+
+        // Bouncycastle doesn't return a default PBEParameterSpec
+        if (isPBE(algorithm) && !"BC".equals(providerName)) {
+            assertNotNull(cipherID + " getParameters()", c.getParameters());
+            assertNotNull(c.getParameters().getParameterSpec(PBEParameterSpec.class));
+        } else {
+            assertNull(cipherID + " getParameters()", c.getParameters());
+        }
+        try {
+            assertNull(cipherID + " getIV()", c.getIV());
+        } catch (NullPointerException e) {
+            // Bouncycastle apparently has a bug here with AESWRAP, et al.
+            if (!("BC".equals(providerName) && isOnlyWrappingAlgorithm(algorithm))) {
+                throw e;
+            }
+        }
+
         c.init(encryptMode, encryptKey, encryptSpec);
         assertEquals(cipherID + " getBlockSize() encryptMode",
                      getExpectedBlockSize(algorithm, encryptMode, providerName), c.getBlockSize());
@@ -826,9 +846,41 @@
         assertEquals(cipherID + " getOutputSize(0) decryptMode",
                      getExpectedOutputSize(algorithm, decryptMode, providerName), c.getOutputSize(0));
 
-        // TODO: test Cipher.getIV()
+        if (isPBE(algorithm)) {
+            if (algorithm.endsWith("RC4")) {
+                assertNull(cipherID + " getIV()", c.getIV());
+            } else {
+                assertNotNull(cipherID + " getIV()", c.getIV());
+            }
+        } else if (decryptSpec instanceof IvParameterSpec) {
+            assertEquals(cipherID + " getIV()",
+                    Arrays.toString(((IvParameterSpec) decryptSpec).getIV()),
+                    Arrays.toString(c.getIV()));
+        } else {
+            try {
+                assertNull(cipherID + " getIV()", c.getIV());
+            } catch (NullPointerException e) {
+                // Bouncycastle apparently has a bug here with AESWRAP, et al.
+                if (!("BC".equals(providerName) && isOnlyWrappingAlgorithm(algorithm))) {
+                    throw e;
+                }
+            }
+        }
 
-        // TODO: test Cipher.getParameters()
+        AlgorithmParameters params = c.getParameters();
+        if (decryptSpec == null) {
+            assertNull(cipherID + " getParameters()", params);
+        } else if (decryptSpec instanceof IvParameterSpec) {
+            IvParameterSpec ivDecryptSpec = (IvParameterSpec) params.getParameterSpec(IvParameterSpec.class);
+            assertEquals(cipherID + " getIV()",
+                    Arrays.toString(((IvParameterSpec) decryptSpec).getIV()),
+                    Arrays.toString(ivDecryptSpec.getIV()));
+        } else if (decryptSpec instanceof PBEParameterSpec) {
+            // Bouncycastle seems to be schizophrenic about whther it returns this or not
+            if (!"BC".equals(providerName)) {
+                assertNotNull(cipherID + " getParameters()", params);
+            }
+        }
 
         assertNull(cipherID, c.getExemptionMechanism());