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());