summaryrefslogtreecommitdiffstats
path: root/luni
diff options
context:
space:
mode:
Diffstat (limited to 'luni')
-rw-r--r--luni/src/benchmark/native/libcore_io_Memory_bench.cpp105
-rwxr-xr-xluni/src/main/files/README.cacerts2
-rw-r--r--luni/src/main/java/android/system/NetlinkSocketAddress.java61
-rw-r--r--luni/src/main/java/android/system/Os.java41
-rw-r--r--luni/src/main/java/android/system/OsConstants.java31
-rw-r--r--luni/src/main/java/android/system/PacketSocketAddress.java64
-rw-r--r--luni/src/main/java/java/io/BufferedInputStream.java2
-rw-r--r--luni/src/main/java/java/io/File.java12
-rw-r--r--luni/src/main/java/java/io/FileDescriptor.java2
-rw-r--r--luni/src/main/java/java/io/InputStreamReader.java4
-rw-r--r--luni/src/main/java/java/io/ObjectInputStream.java7
-rw-r--r--luni/src/main/java/java/io/ObjectStreamField.java8
-rw-r--r--luni/src/main/java/java/io/OutputStreamWriter.java5
-rw-r--r--luni/src/main/java/java/io/RandomAccessFile.java5
-rw-r--r--luni/src/main/java/java/lang/Math.java28
-rw-r--r--luni/src/main/java/java/lang/Package.java26
-rw-r--r--luni/src/main/java/java/lang/Runtime.java25
-rw-r--r--luni/src/main/java/java/lang/StrictMath.java1070
-rw-r--r--luni/src/main/java/java/lang/System.java93
-rw-r--r--luni/src/main/java/java/lang/reflect/Modifier.java19
-rw-r--r--luni/src/main/java/java/net/DatagramSocket.java24
-rw-r--r--luni/src/main/java/java/net/DatagramSocketImpl.java6
-rw-r--r--luni/src/main/java/java/net/Inet6Address.java2
-rw-r--r--luni/src/main/java/java/net/InetAddress.java21
-rw-r--r--luni/src/main/java/java/net/InetSocketAddress.java45
-rw-r--r--luni/src/main/java/java/net/PlainSocketImpl.java10
-rw-r--r--luni/src/main/java/java/net/ServerSocket.java17
-rw-r--r--luni/src/main/java/java/net/Socket.java26
-rw-r--r--luni/src/main/java/java/net/URL.java2
-rw-r--r--luni/src/main/java/java/nio/SelectorImpl.java7
-rw-r--r--luni/src/main/java/java/nio/channels/Selector.java15
-rw-r--r--luni/src/main/java/java/nio/charset/CharsetEncoder.java46
-rw-r--r--luni/src/main/java/java/security/Security.java22
-rw-r--r--luni/src/main/java/java/security/Signature.java16
-rw-r--r--luni/src/main/java/java/text/BreakIterator.java38
-rw-r--r--luni/src/main/java/java/text/ChoiceFormat.java78
-rw-r--r--luni/src/main/java/java/text/Format.java11
-rw-r--r--luni/src/main/java/java/text/IcuIteratorWrapper.java (renamed from luni/src/main/java/java/text/RuleBasedBreakIterator.java)47
-rw-r--r--luni/src/main/java/java/text/MessageFormat.java6
-rw-r--r--luni/src/main/java/java/text/NumberFormat.java8
-rw-r--r--luni/src/main/java/java/text/SimpleDateFormat.java11
-rw-r--r--luni/src/main/java/java/util/Calendar.java4
-rw-r--r--luni/src/main/java/java/util/Collections.java82
-rw-r--r--luni/src/main/java/java/util/Date.java7
-rw-r--r--luni/src/main/java/java/util/EnumMap.java139
-rw-r--r--luni/src/main/java/java/util/Locale.java121
-rw-r--r--luni/src/main/java/java/util/TimeZone.java82
-rw-r--r--luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java20
-rw-r--r--luni/src/main/java/java/util/jar/Manifest.java26
-rw-r--r--luni/src/main/java/java/util/jar/StrictJarFile.java14
-rw-r--r--luni/src/main/java/java/util/logging/FileHandler.java14
-rw-r--r--luni/src/main/java/java/util/logging/SocketHandler.java10
-rw-r--r--luni/src/main/java/java/util/logging/XMLFormatter.java66
-rw-r--r--luni/src/main/java/java/util/zip/GZIPInputStream.java4
-rw-r--r--luni/src/main/java/java/util/zip/Zip64.java401
-rw-r--r--luni/src/main/java/java/util/zip/ZipEntry.java30
-rw-r--r--luni/src/main/java/java/util/zip/ZipFile.java95
-rw-r--r--luni/src/main/java/java/util/zip/ZipInputStream.java33
-rw-r--r--luni/src/main/java/java/util/zip/ZipOutputStream.java281
-rw-r--r--luni/src/main/java/javax/crypto/Cipher.java11
-rw-r--r--luni/src/main/java/javax/crypto/ExemptionMechanism.java3
-rw-r--r--luni/src/main/java/javax/crypto/KeyAgreement.java6
-rw-r--r--luni/src/main/java/javax/crypto/Mac.java6
-rw-r--r--luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java169
-rw-r--r--luni/src/main/java/javax/net/ssl/HttpsURLConnection.java11
-rw-r--r--luni/src/main/java/javax/net/ssl/SSLEngine.java22
-rw-r--r--luni/src/main/java/javax/net/ssl/SSLSocket.java36
-rw-r--r--luni/src/main/java/javax/security/cert/X509Certificate.java4
-rw-r--r--luni/src/main/java/javax/xml/datatype/FactoryFinder.java44
-rw-r--r--luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java47
-rw-r--r--luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java47
-rw-r--r--luni/src/main/java/libcore/icu/DateIntervalFormat.java197
-rw-r--r--luni/src/main/java/libcore/icu/DateTimeFormat.java58
-rw-r--r--luni/src/main/java/libcore/icu/DateUtilsBridge.java177
-rw-r--r--luni/src/main/java/libcore/icu/ICU.java3
-rw-r--r--luni/src/main/java/libcore/icu/LocaleData.java11
-rw-r--r--luni/src/main/java/libcore/icu/NativeBreakIterator.java177
-rw-r--r--luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java360
-rw-r--r--luni/src/main/java/libcore/io/BlockGuardOs.java2
-rw-r--r--luni/src/main/java/libcore/io/ForwardingOs.java13
-rw-r--r--luni/src/main/java/libcore/io/IoBridge.java6
-rw-r--r--luni/src/main/java/libcore/io/IoUtils.java6
-rw-r--r--luni/src/main/java/libcore/io/Os.java13
-rw-r--r--luni/src/main/java/libcore/io/Posix.java16
-rw-r--r--luni/src/main/java/libcore/net/MimeUtils.java65
-rw-r--r--luni/src/main/java/libcore/net/NetworkSecurityPolicy.java66
-rw-r--r--luni/src/main/java/libcore/net/http/ResponseUtils.java90
-rw-r--r--luni/src/main/java/libcore/net/url/FtpURLConnection.java13
-rw-r--r--luni/src/main/java/libcore/util/CharsetUtils.java (renamed from luni/src/main/java/java/nio/charset/Charsets.java)6
-rw-r--r--luni/src/main/java/libcore/util/CountingOutputStream.java59
-rw-r--r--luni/src/main/java/libcore/util/HexEncoding.java99
-rw-r--r--luni/src/main/java/libcore/util/ZoneInfo.java31
-rw-r--r--luni/src/main/java/libcore/util/ZoneInfoDB.java3
-rw-r--r--luni/src/main/java/org/apache/harmony/security/fortress/Services.java36
-rw-r--r--luni/src/main/java/org/apache/harmony/security/utils/JarUtils.java4
-rw-r--r--luni/src/main/java/org/apache/harmony/security/utils/WrappedX509Certificate.java2
-rw-r--r--luni/src/main/native/IcuUtilities.cpp5
-rw-r--r--luni/src/main/native/IcuUtilities.h5
-rw-r--r--luni/src/main/native/Portability.h32
-rw-r--r--luni/src/main/native/Register.cpp4
-rw-r--r--luni/src/main/native/ZipUtilities.cpp7
-rw-r--r--luni/src/main/native/ZipUtilities.h9
-rw-r--r--luni/src/main/native/android_system_OsConstants.cpp34
-rw-r--r--luni/src/main/native/java_lang_StrictMath.cpp65
-rw-r--r--luni/src/main/native/java_lang_StringToReal.cpp151
-rw-r--r--luni/src/main/native/java_lang_System.cpp21
-rw-r--r--luni/src/main/native/java_math_NativeBN.cpp66
-rw-r--r--luni/src/main/native/java_text_Bidi.cpp10
-rw-r--r--luni/src/main/native/java_util_jar_StrictJarFile.cpp70
-rw-r--r--luni/src/main/native/java_util_regex_Matcher.cpp13
-rw-r--r--luni/src/main/native/java_util_regex_Pattern.cpp8
-rw-r--r--luni/src/main/native/java_util_zip_Deflater.cpp11
-rw-r--r--luni/src/main/native/java_util_zip_Inflater.cpp11
-rw-r--r--luni/src/main/native/libcore_icu_AlphabeticIndex.cpp42
-rw-r--r--luni/src/main/native/libcore_icu_DateIntervalFormat.cpp79
-rw-r--r--luni/src/main/native/libcore_icu_ICU.cpp244
-rw-r--r--luni/src/main/native/libcore_icu_NativeBreakIterator.cpp223
-rw-r--r--luni/src/main/native/libcore_icu_NativeCollation.cpp6
-rw-r--r--luni/src/main/native/libcore_icu_NativeConverter.cpp20
-rw-r--r--luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp119
-rw-r--r--luni/src/main/native/libcore_icu_NativeIDN.cpp9
-rw-r--r--luni/src/main/native/libcore_icu_NativeNormalizer.cpp6
-rw-r--r--luni/src/main/native/libcore_icu_NativePluralRules.cpp10
-rw-r--r--luni/src/main/native/libcore_icu_TimeZoneNames.cpp45
-rw-r--r--luni/src/main/native/libcore_icu_Transliterator.cpp12
-rw-r--r--luni/src/main/native/libcore_io_Memory.cpp106
-rw-r--r--luni/src/main/native/libcore_io_Posix.cpp365
-rw-r--r--luni/src/main/native/libcore_util_CharsetUtils.cpp (renamed from luni/src/main/native/java_nio_charset_Charsets.cpp)4
-rw-r--r--luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp9
-rw-r--r--luni/src/main/native/sub.mk11
-rw-r--r--luni/src/test/java/libcore/icu/DateIntervalFormatTest.java133
-rw-r--r--luni/src/test/java/libcore/icu/ICUTest.java24
-rw-r--r--luni/src/test/java/libcore/icu/LocaleDataTest.java12
-rw-r--r--luni/src/test/java/libcore/icu/RelativeDateTimeFormatterTest.java667
-rw-r--r--luni/src/test/java/libcore/io/OsTest.java173
-rw-r--r--luni/src/test/java/libcore/java/io/FileDescriptorTest.java6
-rw-r--r--luni/src/test/java/libcore/java/io/FileInputStreamTest.java2
-rw-r--r--luni/src/test/java/libcore/java/io/RandomAccessFileTest.java57
-rw-r--r--luni/src/test/java/libcore/java/lang/FloatTest.java16
-rwxr-xr-x[-rw-r--r--]luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java14
-rw-r--r--luni/src/test/java/libcore/java/lang/OldClassTest.java5
-rw-r--r--luni/src/test/java/libcore/java/lang/PackageTest.java7
-rw-r--r--luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java2
-rw-r--r--luni/src/test/java/libcore/java/lang/TestPackageAnnotation.java26
-rw-r--r--luni/src/test/java/libcore/java/lang/package-info.java18
-rw-r--r--luni/src/test/java/libcore/java/lang/reflect/FieldTest.java52
-rw-r--r--luni/src/test/java/libcore/java/lang/reflect/MethodTest.java18
-rw-r--r--luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java5
-rw-r--r--luni/src/test/java/libcore/java/net/InetAddressTest.java86
-rw-r--r--luni/src/test/java/libcore/java/net/InetSocketAddressTest.java32
-rw-r--r--luni/src/test/java/libcore/java/net/OldSocketTest.java27
-rw-r--r--luni/src/test/java/libcore/java/net/SocketTest.java33
-rw-r--r--luni/src/test/java/libcore/java/net/URLConnectionTest.java659
-rw-r--r--luni/src/test/java/libcore/java/nio/channels/SelectorTest.java21
-rw-r--r--luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java69
-rw-r--r--luni/src/test/java/libcore/java/nio/charset/SettableCharsetProvider.java57
-rw-r--r--luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java5
-rw-r--r--luni/src/test/java/libcore/java/security/ProviderTest.java2
-rw-r--r--luni/src/test/java/libcore/java/security/SignatureTest.java18
-rw-r--r--luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java40
-rw-r--r--luni/src/test/java/libcore/java/security/cert/X509CRLTest.java10
-rw-r--r--luni/src/test/java/libcore/java/sql/TimestampTest.java8
-rw-r--r--luni/src/test/java/libcore/java/text/BreakIteratorTest.java28
-rw-r--r--luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java14
-rw-r--r--luni/src/test/java/libcore/java/text/NumberFormatTest.java16
-rw-r--r--luni/src/test/java/libcore/java/util/CalendarTest.java72
-rw-r--r--luni/src/test/java/libcore/java/util/CollectionsTest.java91
-rw-r--r--luni/src/test/java/libcore/java/util/CurrencyTest.java2
-rw-r--r--luni/src/test/java/libcore/java/util/LocaleTest.java57
-rw-r--r--luni/src/test/java/libcore/java/util/OldTimeZoneTest.java14
-rw-r--r--luni/src/test/java/libcore/java/util/TimeZoneTest.java46
-rw-r--r--luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java4
-rw-r--r--luni/src/test/java/libcore/java/util/zip/AbstractZipFileTest.java548
-rw-r--r--luni/src/test/java/libcore/java/util/zip/DeflaterTest.java22
-rw-r--r--luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java36
-rw-r--r--luni/src/test/java/libcore/java/util/zip/InflaterTest.java31
-rw-r--r--luni/src/test/java/libcore/java/util/zip/Zip64FileTest.java102
-rw-r--r--luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java34
-rw-r--r--luni/src/test/java/libcore/java/util/zip/ZipFileTest.java491
-rw-r--r--luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java1
-rw-r--r--luni/src/test/java/libcore/javax/crypto/CipherTest.java79
-rw-r--r--luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java70
-rw-r--r--luni/src/test/java/libcore/javax/crypto/KeyGeneratorTest.java9
-rw-r--r--luni/src/test/java/libcore/javax/crypto/MockKeyAgreementSpi.java91
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java373
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/HttpsURLConnectionTest.java115
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java26
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java124
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketFactoryTest.java2
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java2
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java2
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java200
-rw-r--r--luni/src/test/java/libcore/net/MimeUtilsTest.java6
-rw-r--r--luni/src/test/java/libcore/net/NetworkSecurityPolicyTest.java315
-rw-r--r--luni/src/test/java/libcore/net/http/ResponseUtilsTest.java52
-rw-r--r--luni/src/test/java/libcore/util/HexEncodingTest.java76
-rw-r--r--luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java41
-rw-r--r--luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java31
-rw-r--r--luni/src/test/native/libcore_io_Memory_test.cpp134
-rw-r--r--luni/src/test/native/test_openssl_engine.cpp132
-rw-r--r--luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider1
201 files changed, 9526 insertions, 3516 deletions
diff --git a/luni/src/benchmark/native/libcore_io_Memory_bench.cpp b/luni/src/benchmark/native/libcore_io_Memory_bench.cpp
new file mode 100644
index 0000000..0819c27
--- /dev/null
+++ b/luni/src/benchmark/native/libcore_io_Memory_bench.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// The functions we want to benchmark are static, so include the source code.
+#include "luni/src/main/native/libcore_io_Memory.cpp"
+
+#include <benchmark/Benchmark.h>
+
+template<typename T, size_t ALIGN>
+void swap_bench(testing::Benchmark* bench, void (*swap_func)(T*, const T*, size_t),
+ int iters, size_t num_elements) {
+ T* src;
+ T* dst;
+ T* src_elems;
+ T* dst_elems;
+
+ if (ALIGN) {
+ src_elems = new T[num_elements + 1];
+ dst_elems = new T[num_elements + 1];
+
+ src = reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(src_elems) + ALIGN);
+ dst = reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(dst_elems) + ALIGN);
+ } else {
+ src_elems = new T[num_elements];
+ dst_elems = new T[num_elements];
+
+ src = src_elems;
+ dst = dst_elems;
+ }
+
+ memset(dst, 0, sizeof(T) * num_elements);
+ memset(src, 0x12, sizeof(T) * num_elements);
+
+ bench->StartBenchmarkTiming();
+
+ for (int i = 0; i < iters; i++) {
+ swap_func(src, dst, num_elements);
+ }
+
+ bench->StopBenchmarkTiming();
+
+ delete[] src_elems;
+ delete[] dst_elems;
+}
+
+#define AT_COMMON_VALUES \
+ Arg(10)->Arg(100)->Arg(1000)->Arg(1024*10)->Arg(1024*100)
+
+BENCHMARK_WITH_ARG(BM_libcore_swapShorts_aligned, int)->AT_COMMON_VALUES;
+void BM_libcore_swapShorts_aligned::Run(int iters, int num_shorts) {
+ swap_bench<jshort, 0>(this, swapShorts, iters, num_shorts);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapInts_aligned, int)->AT_COMMON_VALUES;
+void BM_libcore_swapInts_aligned::Run(int iters, int num_ints) {
+ swap_bench<jint, 0>(this, swapInts, iters, num_ints);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapLongs_aligned, int)->AT_COMMON_VALUES;
+void BM_libcore_swapLongs_aligned::Run(int iters, int num_longs) {
+ swap_bench<jlong, 0>(this, swapLongs, iters, num_longs);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapShorts_unaligned1, int)->AT_COMMON_VALUES;
+void BM_libcore_swapShorts_unaligned1::Run(int iters, int num_shorts) {
+ swap_bench<jshort, 1>(this, swapShorts, iters, num_shorts);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapInts_unaligned1, int)->AT_COMMON_VALUES;
+void BM_libcore_swapInts_unaligned1::Run(int iters, int num_ints) {
+ swap_bench<jint, 1>(this, swapInts, iters, num_ints);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapLongs_unaligned1, int)->AT_COMMON_VALUES;
+void BM_libcore_swapLongs_unaligned1::Run(int iters, int num_longs) {
+ swap_bench<jlong, 1>(this, swapLongs, iters, num_longs);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapShorts_unaligned2, int)->AT_COMMON_VALUES;
+void BM_libcore_swapShorts_unaligned2::Run(int iters, int num_shorts) {
+ swap_bench<jshort, 2>(this, swapShorts, iters, num_shorts);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapInts_unaligned2, int)->AT_COMMON_VALUES;
+void BM_libcore_swapInts_unaligned2::Run(int iters, int num_ints) {
+ swap_bench<jint, 2>(this, swapInts, iters, num_ints);
+}
+
+BENCHMARK_WITH_ARG(BM_libcore_swapLongs_unaligned2, int)->AT_COMMON_VALUES;
+void BM_libcore_swapLongs_unaligned2::Run(int iters, int num_longs) {
+ swap_bench<jlong, 2>(this, swapLongs, iters, num_longs);
+}
diff --git a/luni/src/main/files/README.cacerts b/luni/src/main/files/README.cacerts
index e905a68..ca5c570 100755
--- a/luni/src/main/files/README.cacerts
+++ b/luni/src/main/files/README.cacerts
@@ -1,7 +1,7 @@
The filenames in the cacerts directory are in the format of <hash>.<n>
where "hash" is the subject hash produced by:
- openssl x509 -subject_hash -in filename
+ openssl x509 -subject_hash_old -in filename
and the "n" is a unique integer identifier starting at 0 to deal
with collisions. See OpenSSL's c_rehash manpage for details.
diff --git a/luni/src/main/java/android/system/NetlinkSocketAddress.java b/luni/src/main/java/android/system/NetlinkSocketAddress.java
new file mode 100644
index 0000000..af78cd0
--- /dev/null
+++ b/luni/src/main/java/android/system/NetlinkSocketAddress.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.system;
+
+import libcore.util.Objects;
+import java.net.SocketAddress;
+
+/**
+ * Netlink socket address.
+ *
+ * Corresponds to Linux's {@code struct sockaddr_nl} from
+ * <a href="https://github.com/torvalds/linux/blob/master/include/uapi/linux/netlink.h">&lt;linux/netlink.h&gt;</a>.
+ *
+ * @hide
+ */
+public final class NetlinkSocketAddress extends SocketAddress {
+ /** port ID */
+ private final int nlPortId;
+
+ /** multicast groups mask */
+ private final int nlGroupsMask;
+
+ public NetlinkSocketAddress() {
+ this(0, 0);
+ }
+
+ public NetlinkSocketAddress(int nlPortId) {
+ this(nlPortId, 0);
+ }
+
+ public NetlinkSocketAddress(int nlPortId, int nlGroupsMask) {
+ this.nlPortId = nlPortId;
+ this.nlGroupsMask = nlGroupsMask;
+ }
+
+ public int getPortId() {
+ return nlPortId;
+ }
+
+ public int getGroupsMask() {
+ return nlGroupsMask;
+ }
+
+ @Override public String toString() {
+ return Objects.toString(this);
+ }
+}
diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java
index 9d6dc1b..fcecf18 100644
--- a/luni/src/main/java/android/system/Os.java
+++ b/luni/src/main/java/android/system/Os.java
@@ -54,6 +54,8 @@ public final class Os {
*/
public static void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { Libcore.os.bind(fd, address, port); }
+ /** @hide */ public static void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { Libcore.os.bind(fd, address); }
+
/**
* See <a href="http://man7.org/linux/man-pages/man2/chmod.2.html">chmod(2)</a>.
*/
@@ -74,6 +76,8 @@ public final class Os {
*/
public static void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { Libcore.os.connect(fd, address, port); }
+ /** @hide */ public static void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { Libcore.os.connect(fd, address); }
+
/**
* See <a href="http://man7.org/linux/man-pages/man2/dup.2.html">dup(2)</a>.
*/
@@ -109,9 +113,9 @@ public final class Os {
*/
public static void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException { Libcore.os.fchown(fd, uid, gid); }
- /** @hide */ public static int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException { return Libcore.os.fcntlVoid(fd, cmd); }
- /** @hide */ public static int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException { return Libcore.os.fcntlLong(fd, cmd, arg); }
/** @hide */ public static int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException, InterruptedIOException { return Libcore.os.fcntlFlock(fd, cmd, arg); }
+ /** @hide */ public static int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException { return Libcore.os.fcntlInt(fd, cmd, arg); }
+ /** @hide */ public static int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException { return Libcore.os.fcntlVoid(fd, cmd); }
/**
* See <a href="http://man7.org/linux/man-pages/man2/fdatasync.2.html">fdatasync(2)</a>.
@@ -171,6 +175,11 @@ public final class Os {
public static SocketAddress getpeername(FileDescriptor fd) throws ErrnoException { return Libcore.os.getpeername(fd); }
/**
+ * See <a href="http://man7.org/linux/man-pages/man2/getpgid.2.html">getpgid(2)</a>.
+ */
+ /** @hide */ public static int getpgid(int pid) throws ErrnoException { return Libcore.os.getpgid(pid); }
+
+ /**
* See <a href="http://man7.org/linux/man-pages/man2/getpid.2.html">getpid(2)</a>.
*/
public static int getpid() { return Libcore.os.getpid(); }
@@ -302,10 +311,16 @@ public final class Os {
/**
* See <a href="http://man7.org/linux/man-pages/man2/pipe.2.html">pipe(2)</a>.
*/
- public static FileDescriptor[] pipe() throws ErrnoException { return Libcore.os.pipe(); }
+ public static FileDescriptor[] pipe() throws ErrnoException { return Libcore.os.pipe2(0); }
+
+ /** @hide */ public static FileDescriptor[] pipe2(int flags) throws ErrnoException { return Libcore.os.pipe2(flags); }
/**
* See <a href="http://man7.org/linux/man-pages/man2/poll.2.html">poll(2)</a>.
+ *
+ * <p>Note that in Lollipop this could throw an {@code ErrnoException} with {@code EINTR}.
+ * In later releases, the implementation will automatically just restart the system call with
+ * an appropriately reduced timeout.
*/
public static int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException { return Libcore.os.poll(fds, timeoutMs); }
@@ -395,6 +410,11 @@ public final class Os {
public static int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return Libcore.os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); }
/**
+ * See <a href="http://man7.org/linux/man-pages/man2/sendto.2.html">sendto(2)</a>.
+ */
+ /** @hide */ public static int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException { return Libcore.os.sendto(fd, bytes, byteOffset, byteCount, flags, address); }
+
+ /**
* See <a href="http://man7.org/linux/man-pages/man2/setegid.2.html">setegid(2)</a>.
*/
public static void setegid(int egid) throws ErrnoException { Libcore.os.setegid(egid); }
@@ -415,6 +435,21 @@ public final class Os {
public static void setgid(int gid) throws ErrnoException { Libcore.os.setgid(gid); }
/**
+ * See <a href="http://man7.org/linux/man-pages/man2/setpgid.2.html">setpgid(2)</a>.
+ */
+ /** @hide */ public static void setpgid(int pid, int pgid) throws ErrnoException { Libcore.os.setpgid(pid, pgid); }
+
+ /**
+ * See <a href="http://man7.org/linux/man-pages/man2/setregid.2.html">setregid(2)</a>.
+ */
+ /** @hide */ public static void setregid(int rgid, int egid) throws ErrnoException { Libcore.os.setregid(rgid, egid); }
+
+ /**
+ * See <a href="http://man7.org/linux/man-pages/man2/setreuid.2.html">setreuid(2)</a>.
+ */
+ /** @hide */ public static void setreuid(int ruid, int euid) throws ErrnoException { Libcore.os.setreuid(ruid, euid); }
+
+ /**
* See <a href="http://man7.org/linux/man-pages/man2/setsid.2.html">setsid(2)</a>.
*/
public static int setsid() throws ErrnoException { return Libcore.os.setsid(); }
diff --git a/luni/src/main/java/android/system/OsConstants.java b/luni/src/main/java/android/system/OsConstants.java
index c758eb7..c0d31e5 100644
--- a/luni/src/main/java/android/system/OsConstants.java
+++ b/luni/src/main/java/android/system/OsConstants.java
@@ -95,6 +95,8 @@ public final class OsConstants {
public static final int AF_INET = placeholder();
public static final int AF_INET6 = placeholder();
+ /** @hide */ public static final int AF_NETLINK = placeholder();
+ /** @hide */ public static final int AF_PACKET = placeholder();
public static final int AF_UNIX = placeholder();
public static final int AF_UNSPEC = placeholder();
public static final int AI_ADDRCONFIG = placeholder();
@@ -104,6 +106,8 @@ public final class OsConstants {
public static final int AI_NUMERICSERV = placeholder();
public static final int AI_PASSIVE = placeholder();
public static final int AI_V4MAPPED = placeholder();
+ /** @hide */ public static final int ARPHRD_ETHER = placeholder();
+ /** @hide */ public static final int ARPHRD_LOOPBACK = placeholder();
public static final int CAP_AUDIT_CONTROL = placeholder();
public static final int CAP_AUDIT_WRITE = placeholder();
public static final int CAP_BLOCK_SUSPEND = placeholder();
@@ -227,6 +231,9 @@ public final class OsConstants {
public static final int ESPIPE = placeholder();
public static final int ESRCH = placeholder();
public static final int ESTALE = placeholder();
+ /** @hide */ public static final int ETH_P_ARP = placeholder();
+ /** @hide */ public static final int ETH_P_IP = placeholder();
+ /** @hide */ public static final int ETH_P_IPV6 = placeholder();
public static final int ETIME = placeholder();
public static final int ETIMEDOUT = placeholder();
public static final int ETXTBSY = placeholder();
@@ -324,6 +331,7 @@ public final class OsConstants {
public static final int MS_ASYNC = placeholder();
public static final int MS_INVALIDATE = placeholder();
public static final int MS_SYNC = placeholder();
+ /** @hide */ public static final int NETLINK_ROUTE = placeholder();
public static final int NI_DGRAM = placeholder();
public static final int NI_NAMEREQD = placeholder();
public static final int NI_NOFQDN = placeholder();
@@ -331,6 +339,7 @@ public final class OsConstants {
public static final int NI_NUMERICSERV = placeholder();
public static final int O_ACCMODE = placeholder();
public static final int O_APPEND = placeholder();
+ /** @hide */ public static final int O_CLOEXEC = placeholder();
public static final int O_CREAT = placeholder();
public static final int O_EXCL = placeholder();
public static final int O_NOCTTY = placeholder();
@@ -364,6 +373,19 @@ public final class OsConstants {
public static final int RT_SCOPE_NOWHERE = placeholder();
public static final int RT_SCOPE_SITE = placeholder();
public static final int RT_SCOPE_UNIVERSE = placeholder();
+ /** @hide */ public static final int RTMGRP_IPV4_IFADDR = placeholder();
+ /** @hide */ public static final int RTMGRP_IPV4_MROUTE = placeholder();
+ /** @hide */ public static final int RTMGRP_IPV4_ROUTE = placeholder();
+ /** @hide */ public static final int RTMGRP_IPV4_RULE = placeholder();
+ /** @hide */ public static final int RTMGRP_IPV6_IFADDR = placeholder();
+ /** @hide */ public static final int RTMGRP_IPV6_IFINFO = placeholder();
+ /** @hide */ public static final int RTMGRP_IPV6_MROUTE = placeholder();
+ /** @hide */ public static final int RTMGRP_IPV6_PREFIX = placeholder();
+ /** @hide */ public static final int RTMGRP_IPV6_ROUTE = placeholder();
+ /** @hide */ public static final int RTMGRP_LINK = placeholder();
+ /** @hide */ public static final int RTMGRP_NEIGH = placeholder();
+ /** @hide */ public static final int RTMGRP_NOTIFY = placeholder();
+ /** @hide */ public static final int RTMGRP_TC = placeholder();
public static final int SEEK_CUR = placeholder();
public static final int SEEK_END = placeholder();
public static final int SEEK_SET = placeholder();
@@ -433,6 +455,15 @@ public final class OsConstants {
public static final int STDERR_FILENO = placeholder();
public static final int STDIN_FILENO = placeholder();
public static final int STDOUT_FILENO = placeholder();
+ /** @hide */ public static final int ST_MANDLOCK = placeholder();
+ /** @hide */ public static final int ST_NOATIME = placeholder();
+ /** @hide */ public static final int ST_NODEV = placeholder();
+ /** @hide */ public static final int ST_NODIRATIME = placeholder();
+ /** @hide */ public static final int ST_NOEXEC = placeholder();
+ /** @hide */ public static final int ST_NOSUID = placeholder();
+ /** @hide */ public static final int ST_RDONLY = placeholder();
+ /** @hide */ public static final int ST_RELATIME = placeholder();
+ /** @hide */ public static final int ST_SYNCHRONOUS = placeholder();
public static final int S_IFBLK = placeholder();
public static final int S_IFCHR = placeholder();
public static final int S_IFDIR = placeholder();
diff --git a/luni/src/main/java/android/system/PacketSocketAddress.java b/luni/src/main/java/android/system/PacketSocketAddress.java
new file mode 100644
index 0000000..510771c
--- /dev/null
+++ b/luni/src/main/java/android/system/PacketSocketAddress.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.system;
+
+import libcore.util.Objects;
+import java.net.SocketAddress;
+
+/**
+ * Packet socket address.
+ *
+ * Corresponds to Linux's {@code struct sockaddr_ll}.
+ *
+ * @hide
+ */
+public final class PacketSocketAddress extends SocketAddress {
+ /** Protocol. An Ethernet protocol type, e.g., {@code ETH_P_IPV6}. */
+ public short sll_protocol;
+
+ /** Interface index. */
+ public int sll_ifindex;
+
+ /** ARP hardware type. One of the {@code ARPHRD_*} constants. */
+ public short sll_hatype;
+
+ /** Packet type. One of the {@code PACKET_*} constants, such as {@code PACKET_OTHERHOST}. */
+ public byte sll_pkttype;
+
+ /** Hardware address. */
+ public byte[] sll_addr;
+
+ /** Constructs a new PacketSocketAddress. */
+ public PacketSocketAddress(short sll_protocol, int sll_ifindex,
+ short sll_hatype, byte sll_pkttype, byte[] sll_addr) {
+ this.sll_protocol = sll_protocol;
+ this.sll_ifindex = sll_ifindex;
+ this.sll_hatype = sll_hatype;
+ this.sll_pkttype = sll_pkttype;
+ this.sll_addr = sll_addr;
+ }
+
+ /** Constructs a new PacketSocketAddress suitable for binding to. */
+ public PacketSocketAddress(short sll_protocol, int sll_ifindex) {
+ this(sll_protocol, sll_ifindex, (short) 0, (byte) 0, null);
+ }
+
+ /** Constructs a new PacketSocketAddress suitable for sending to. */
+ public PacketSocketAddress(int sll_ifindex, byte[] sll_addr) {
+ this((short) 0, sll_ifindex, (short) 0, (byte) 0, sll_addr);
+ }
+}
diff --git a/luni/src/main/java/java/io/BufferedInputStream.java b/luni/src/main/java/java/io/BufferedInputStream.java
index 85236b6..ec43720 100644
--- a/luni/src/main/java/java/io/BufferedInputStream.java
+++ b/luni/src/main/java/java/io/BufferedInputStream.java
@@ -150,7 +150,7 @@ public class BufferedInputStream extends FilterInputStream {
if (result > 0) {
markpos = -1;
pos = 0;
- count = result == -1 ? 0 : result;
+ count = result;
}
return result;
}
diff --git a/luni/src/main/java/java/io/File.java b/luni/src/main/java/java/io/File.java
index d107c28..0592345 100644
--- a/luni/src/main/java/java/io/File.java
+++ b/luni/src/main/java/java/io/File.java
@@ -57,12 +57,6 @@ public class File implements Serializable, Comparable<File> {
private static final long serialVersionUID = 301077366599181567L;
/**
- * Reusing a Random makes temporary filenames slightly harder to predict.
- * (Random is thread-safe.)
- */
- private static final Random tempFileRandom = new Random();
-
- /**
* The system-dependent character used to separate components in filenames ('/').
* Use of this (rather than hard-coding '/') helps portability to other operating systems.
*
@@ -129,6 +123,8 @@ public class File implements Serializable, Comparable<File> {
*
* @param path
* the path to be used for the file.
+ * @throws NullPointerException
+ * if {@code path} is {@code null}.
*/
public File(String path) {
this.path = fixSlashes(path);
@@ -167,6 +163,8 @@ public class File implements Serializable, Comparable<File> {
* @param uri
* the Unified Resource Identifier that is used to construct this
* file.
+ * @throws NullPointerException
+ * if {@code uri == null}.
* @throws IllegalArgumentException
* if {@code uri} does not comply with the conditions above.
* @see #toURI
@@ -1002,7 +1000,7 @@ public class File implements Serializable, Comparable<File> {
}
File result;
do {
- result = new File(tmpDirFile, prefix + tempFileRandom.nextInt() + suffix);
+ result = new File(tmpDirFile, prefix + Math.randomIntInternal() + suffix);
} while (!result.createNewFile());
return result;
}
diff --git a/luni/src/main/java/java/io/FileDescriptor.java b/luni/src/main/java/java/io/FileDescriptor.java
index eba0e4d..be94c52 100644
--- a/luni/src/main/java/java/io/FileDescriptor.java
+++ b/luni/src/main/java/java/io/FileDescriptor.java
@@ -108,7 +108,7 @@ public final class FileDescriptor {
/**
* @hide internal use only
*/
- public boolean isSocket() {
+ public final boolean isSocket$() {
return isSocket(descriptor);
}
diff --git a/luni/src/main/java/java/io/InputStreamReader.java b/luni/src/main/java/java/io/InputStreamReader.java
index d57b916..a4cacf2 100644
--- a/luni/src/main/java/java/io/InputStreamReader.java
+++ b/luni/src/main/java/java/io/InputStreamReader.java
@@ -101,7 +101,9 @@ public class InputStreamReader extends Reader {
*/
public InputStreamReader(InputStream in, CharsetDecoder dec) {
super(in);
- dec.averageCharsPerByte();
+ if (dec == null) {
+ throw new NullPointerException("dec == null");
+ }
this.in = in;
decoder = dec;
bytes.limit(0);
diff --git a/luni/src/main/java/java/io/ObjectInputStream.java b/luni/src/main/java/java/io/ObjectInputStream.java
index 3a89b52..cd267b2 100644
--- a/luni/src/main/java/java/io/ObjectInputStream.java
+++ b/luni/src/main/java/java/io/ObjectInputStream.java
@@ -1977,7 +1977,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Objec
// original/outside caller
if (++nestedLevels == 1) {
// Remember the caller's class loader
- callerClassLoader = VMStack.getClosestUserClassLoader(bootstrapLoader, systemLoader);
+ callerClassLoader = VMStack.getClosestUserClassLoader();
}
result = readNonPrimitiveContent(unshared);
@@ -2014,9 +2014,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Objec
return result;
}
- private static final ClassLoader bootstrapLoader = Object.class.getClassLoader();
- private static final ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
-
/**
* Method to be overridden by subclasses to read the next object from the
* source stream.
@@ -2258,8 +2255,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Objec
if (cls == null) {
// not primitive class
- // Use the first non-null ClassLoader on the stack. If null, use
- // the system class loader
cls = Class.forName(className, false, callerClassLoader);
}
}
diff --git a/luni/src/main/java/java/io/ObjectStreamField.java b/luni/src/main/java/java/io/ObjectStreamField.java
index 78a6903..2a9b107 100644
--- a/luni/src/main/java/java/io/ObjectStreamField.java
+++ b/luni/src/main/java/java/io/ObjectStreamField.java
@@ -58,13 +58,7 @@ public class ObjectStreamField implements Comparable<Object> {
* if {@code name} or {@code cl} is {@code null}.
*/
public ObjectStreamField(String name, Class<?> cl) {
- if (name == null) {
- throw new NullPointerException("name == null");
- } else if (cl == null) {
- throw new NullPointerException("cl == null");
- }
- this.name = name;
- this.type = new WeakReference<Class<?>>(cl);
+ this(name, cl, false);
}
/**
diff --git a/luni/src/main/java/java/io/OutputStreamWriter.java b/luni/src/main/java/java/io/OutputStreamWriter.java
index bc8710d..8a639e7 100644
--- a/luni/src/main/java/java/io/OutputStreamWriter.java
+++ b/luni/src/main/java/java/io/OutputStreamWriter.java
@@ -116,7 +116,10 @@ public class OutputStreamWriter extends Writer {
*/
public OutputStreamWriter(OutputStream out, CharsetEncoder charsetEncoder) {
super(out);
- charsetEncoder.charset();
+ if (charsetEncoder == null) {
+ throw new NullPointerException("charsetEncoder == null");
+ }
+
this.out = out;
encoder = charsetEncoder;
}
diff --git a/luni/src/main/java/java/io/RandomAccessFile.java b/luni/src/main/java/java/io/RandomAccessFile.java
index da99765..0e4fa4f 100644
--- a/luni/src/main/java/java/io/RandomAccessFile.java
+++ b/luni/src/main/java/java/io/RandomAccessFile.java
@@ -160,7 +160,6 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable {
synchronized (this) {
if (channel != null && channel.isOpen()) {
channel.close();
- channel = null;
}
IoBridge.closeAndSignalBlockedThreads(fd);
}
@@ -185,6 +184,10 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable {
* changes made to this file's file pointer offset are also visible in the
* file channel's position and vice versa.
*
+ * Closing the channel closes the RandomAccessFile as well. The instance
+ * of FileChannel returned is always the same even if the RandomAccessFile
+ * or the FileChannel have been closed.
+ *
* @return this file's file channel instance.
*/
public final synchronized FileChannel getChannel() {
diff --git a/luni/src/main/java/java/lang/Math.java b/luni/src/main/java/java/lang/Math.java
index 86df784..7203566 100644
--- a/luni/src/main/java/java/lang/Math.java
+++ b/luni/src/main/java/java/lang/Math.java
@@ -35,7 +35,9 @@ public final class Math {
*/
public static final double PI = 3.141592653589793;
- private static Random random;
+ private static class NoImagePreloadHolder {
+ private static final Random INSTANCE = new Random();
+ }
/**
* Prevents this class from being instantiated.
@@ -875,11 +877,25 @@ public final class Math {
*
* @return a pseudo-random number.
*/
- public static synchronized double random() {
- if (random == null) {
- random = new Random();
- }
- return random.nextDouble();
+ public static double random() {
+ return NoImagePreloadHolder.INSTANCE.nextDouble();
+ }
+
+ /**
+ * Set the seed for the pseudo random generator used by {@link #random()}
+ * and {@link #randomIntInternal()}.
+ *
+ * @hide for internal use only.
+ */
+ public static void setRandomSeedInternal(long seed) {
+ NoImagePreloadHolder.INSTANCE.setSeed(seed);
+ }
+
+ /**
+ * @hide for internal use only.
+ */
+ public static int randomIntInternal() {
+ return NoImagePreloadHolder.INSTANCE.nextInt();
}
/**
diff --git a/luni/src/main/java/java/lang/Package.java b/luni/src/main/java/java/lang/Package.java
index 7e30883..3c6c39c 100644
--- a/luni/src/main/java/java/lang/Package.java
+++ b/luni/src/main/java/java/lang/Package.java
@@ -51,6 +51,7 @@ import java.net.URL;
public class Package implements AnnotatedElement {
private static final Annotation[] NO_ANNOTATIONS = new Annotation[0];
+ private final ClassLoader classLoader;
private final String name;
private final String specTitle;
private final String specVersion;
@@ -60,8 +61,10 @@ public class Package implements AnnotatedElement {
private final String implVendor;
private final URL sealBase;
- Package(String name, String specTitle, String specVersion, String specVendor,
- String implTitle, String implVersion, String implVendor, URL sealBase) {
+ Package(ClassLoader classLoader, String name, String specTitle, String specVersion,
+ String specVendor, String implTitle, String implVersion, String implVendor,
+ URL sealBase) {
+ this.classLoader = classLoader;
this.name = name;
this.specTitle = specTitle;
this.specVersion = specVersion;
@@ -96,7 +99,8 @@ public class Package implements AnnotatedElement {
*/
public Annotation[] getAnnotations() {
try {
- Class<?> c = Class.forName(getName() + ".package-info");
+ Class<?> c = Class.forName(getName() + ".package-info", false /* initialize */,
+ classLoader);
return c.getAnnotations();
} catch (Exception ex) {
return NO_ANNOTATIONS;
@@ -175,11 +179,11 @@ public class Package implements AnnotatedElement {
* @see ClassLoader#getPackage(java.lang.String)
*/
public static Package getPackage(String packageName) {
- ClassLoader classloader = VMStack.getCallingClassLoader();
- if (classloader == null) {
- classloader = ClassLoader.getSystemClassLoader();
+ ClassLoader classLoader = VMStack.getCallingClassLoader();
+ if (classLoader == null) {
+ classLoader = ClassLoader.getSystemClassLoader();
}
- return classloader.getPackage(packageName);
+ return classLoader.getPackage(packageName);
}
/**
@@ -189,11 +193,11 @@ public class Package implements AnnotatedElement {
* @see ClassLoader#getPackages
*/
public static Package[] getPackages() {
- ClassLoader classloader = VMStack.getCallingClassLoader();
- if (classloader == null) {
- classloader = ClassLoader.getSystemClassLoader();
+ ClassLoader classLoader = VMStack.getCallingClassLoader();
+ if (classLoader == null) {
+ classLoader = ClassLoader.getSystemClassLoader();
}
- return classloader.getPackages();
+ return classLoader.getPackages();
}
/**
diff --git a/luni/src/main/java/java/lang/Runtime.java b/luni/src/main/java/java/lang/Runtime.java
index a3cb83e..3ddacf7 100644
--- a/luni/src/main/java/java/lang/Runtime.java
+++ b/luni/src/main/java/java/lang/Runtime.java
@@ -357,14 +357,11 @@ public class Runtime {
*/
void loadLibrary(String libraryName, ClassLoader loader) {
if (loader != null) {
+ // TODO: We shouldn't assume that we know default linker search logic.
String filename = loader.findLibrary(libraryName);
if (filename == null) {
- // It's not necessarily true that the ClassLoader used
- // System.mapLibraryName, but the default setup does, and it's
- // misleading to say we didn't find "libMyLibrary.so" when we
- // actually searched for "liblibMyLibrary.so.so".
- throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
- System.mapLibraryName(libraryName) + "\"");
+ // The dynamic linker might still find the library by name.
+ filename = System.mapLibraryName(libraryName);
}
String error = doLoad(filename, loader);
if (error != null) {
@@ -418,19 +415,27 @@ public class Runtime {
// So, find out what the native library search path is for the ClassLoader in question...
String ldLibraryPath = null;
- if (loader != null && loader instanceof BaseDexClassLoader) {
- ldLibraryPath = ((BaseDexClassLoader) loader).getLdLibraryPath();
+ String dexPath = null;
+ if (loader == null) {
+ // We use the given library path for the boot class loader. This is the path
+ // also used in loadLibraryName if loader is null.
+ ldLibraryPath = System.getProperty("java.library.path");
+ } else if (loader instanceof BaseDexClassLoader) {
+ BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
+ ldLibraryPath = dexClassLoader.getLdLibraryPath();
+ dexPath = dexClassLoader.getDexPath();
}
// nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless
// of how many ClassLoaders are in the system, but dalvik doesn't support synchronized
// internal natives.
synchronized (this) {
- return nativeLoad(name, loader, ldLibraryPath);
+ return nativeLoad(name, loader, ldLibraryPath, dexPath);
}
}
// TODO: should be synchronized, but dalvik doesn't support synchronized internal natives.
- private static native String nativeLoad(String filename, ClassLoader loader, String ldLibraryPath);
+ private static native String nativeLoad(String filename, ClassLoader loader,
+ String ldLibraryPath, String dexPath);
/**
* Provides a hint to the VM that it would be useful to attempt
diff --git a/luni/src/main/java/java/lang/StrictMath.java b/luni/src/main/java/java/lang/StrictMath.java
index f409c06..2e848f2 100644
--- a/luni/src/main/java/java/lang/StrictMath.java
+++ b/luni/src/main/java/java/lang/StrictMath.java
@@ -15,6 +15,18 @@
* limitations under the License.
*/
+/*
+ * acos, asin, atan, cosh, sinh, tanh, exp, expm1, log, log10, log1p, and cbrt
+ * have been implemented with the following license.
+ * ====================================================
+ * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
package java.lang;
/**
@@ -102,6 +114,21 @@ public final class StrictMath {
return Math.abs(l);
}
+ private static final double PIO2_HI = 1.57079632679489655800e+00;
+ private static final double PIO2_LO = 6.12323399573676603587e-17;
+ private static final double PS0 = 1.66666666666666657415e-01;
+ private static final double PS1 = -3.25565818622400915405e-01;
+ private static final double PS2 = 2.01212532134862925881e-01;
+ private static final double PS3 = -4.00555345006794114027e-02;
+ private static final double PS4 = 7.91534994289814532176e-04;
+ private static final double PS5 = 3.47933107596021167570e-05;
+ private static final double QS1 = -2.40339491173441421878e+00;
+ private static final double QS2 = 2.02094576023350569471e+00;
+ private static final double QS3 = -6.88283971605453293030e-01;
+ private static final double QS4 = 7.70381505559019352791e-02;
+ private static final double HUGE = 1.000e+300;
+ private static final double PIO4_HI = 7.85398163397448278999e-01;
+
/**
* Returns the closest double approximation of the arc cosine of the
* argument within the range {@code [0..pi]}.
@@ -113,11 +140,62 @@ public final class StrictMath {
* <li>{@code acos(NaN) = NaN}</li>
* </ul>
*
- * @param d
+ * @param x
* the value to compute arc cosine of.
* @return the arc cosine of the argument.
*/
- public static native double acos(double d);
+ public static double acos(double x) {
+ double z, p, q, r, w, s, c, df;
+ int hx, ix;
+ final long bits = Double.doubleToRawLongBits(x);
+ hx = (int) (bits >>> 32);
+ ix = hx & 0x7fffffff;
+ if (ix >= 0x3ff00000) { /* |x| >= 1 */
+ if ((((ix - 0x3ff00000) | ((int) bits))) == 0) { /* |x|==1 */
+ if (hx > 0) {
+ return 0.0; /* ieee_acos(1) = 0 */
+ } else {
+ return 3.14159265358979311600e+00 + 2.0 * PIO2_LO; /* ieee_acos(-1)= pi */
+ }
+ }
+ return (x - x) / (x - x); /* ieee_acos(|x|>1) is NaN */
+ }
+
+ if (ix < 0x3fe00000) { /* |x| < 0.5 */
+ if (ix <= 0x3c600000) {
+ return PIO2_HI + PIO2_LO;/* if|x|<2**-57 */
+ }
+
+ z = x * x;
+ p = z * (PS0 + z
+ * (PS1 + z * (PS2 + z * (PS3 + z * (PS4 + z * PS5)))));
+ q = 1.00000000000000000000e+00 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4)));
+ r = p / q;
+ return PIO2_HI - (x - (PIO2_LO - x * r));
+ } else if (hx < 0) { /* x < -0.5 */
+ z = (1.00000000000000000000e+00 + x) * 0.5;
+ p = z * (PS0 + z
+ * (PS1 + z * (PS2 + z * (PS3 + z * (PS4 + z * PS5)))));
+ q = 1.00000000000000000000e+00 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4)));
+ s = StrictMath.sqrt(z);
+ r = p / q;
+ w = r * s - PIO2_LO;
+ return 3.14159265358979311600e+00 - 2.0 * (s + w);
+ } else { /* x > 0.5 */
+ z = (1.00000000000000000000e+00 - x) * 0.5;
+ s = StrictMath.sqrt(z);
+ df = s;
+ df = Double.longBitsToDouble(
+ Double.doubleToRawLongBits(df) & 0xffffffffL << 32);
+ c = (z - df * df) / (s + df);
+ p = z * (PS0 + z
+ * (PS1 + z * (PS2 + z * (PS3 + z * (PS4 + z * PS5)))));
+ q = 1.00000000000000000000e+00 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4)));
+ r = p / q;
+ w = r * s + c;
+ return 2.0 * (df + w);
+ }
+ }
/**
* Returns the closest double approximation of the arc sine of the argument
@@ -130,11 +208,75 @@ public final class StrictMath {
* <li>{@code asin(NaN) = NaN}</li>
* </ul>
*
- * @param d
+ * @param x
* the value whose arc sine has to be computed.
* @return the arc sine of the argument.
*/
- public static native double asin(double d);
+ public static double asin(double x) {
+ double t, w, p, q, c, r, s;
+ int hx, ix;
+ final long bits = Double.doubleToRawLongBits(x);
+ hx = (int) (bits >>> 32);
+ ix = hx & 0x7fffffff;
+ if (ix >= 0x3ff00000) { /* |x|>= 1 */
+ if ((((ix - 0x3ff00000) | ((int) bits))) == 0) {
+ /* ieee_asin(1)=+-pi/2 with inexact */
+ return x * PIO2_HI + x * PIO2_LO;
+ }
+ return (x - x) / (x - x); /* ieee_asin(|x|>1) is NaN */
+ } else if (ix < 0x3fe00000) { /* |x|<0.5 */
+ if (ix < 0x3e400000) { /* if |x| < 2**-27 */
+ if (HUGE + x > 1.00000000000000000000e+00) {
+ return x;/* return x with inexact if x!=0 */
+ }
+ } else {
+ t = x * x;
+ p = t * (PS0 + t
+ * (PS1 + t * (PS2 + t * (PS3 + t * (PS4 + t * PS5)))));
+ q = 1.00000000000000000000e+00 + t * (QS1 + t * (QS2 + t * (QS3 + t * QS4)));
+ w = p / q;
+ return x + x * w;
+ }
+ }
+ /* 1> |x|>= 0.5 */
+ w = 1.00000000000000000000e+00 - Math.abs(x);
+ t = w * 0.5;
+ p = t * (PS0 + t * (PS1 + t * (PS2 + t * (PS3 + t * (PS4 + t * PS5)))));
+ q = 1.00000000000000000000e+00 + t * (QS1 + t * (QS2 + t * (QS3 + t * QS4)));
+ s = StrictMath.sqrt(t);
+ if (ix >= 0x3FEF3333) { /* if |x| > 0.975 */
+ w = p / q;
+ t = PIO2_HI - (2.0 * (s + s * w) - PIO2_LO);
+ } else {
+ w = s;
+ w = Double.longBitsToDouble(
+ Double.doubleToRawLongBits(w) & 0xffffffffL << 32);
+ c = (t - w * w) / (s + w);
+ r = p / q;
+ p = 2.0 * s * r - (PIO2_LO - 2.0 * c);
+ q = PIO4_HI - 2.0 * w;
+ t = PIO4_HI - (p - q);
+ }
+ return (hx > 0) ? t : -t;
+ }
+
+ private static final double[] ATANHI = { 4.63647609000806093515e-01,
+ 7.85398163397448278999e-01, 9.82793723247329054082e-01,
+ 1.57079632679489655800e+00 };
+ private static final double[] ATANLO = { 2.26987774529616870924e-17,
+ 3.06161699786838301793e-17, 1.39033110312309984516e-17,
+ 6.12323399573676603587e-17 };
+ private static final double AT0 = 3.33333333333329318027e-01;
+ private static final double AT1 = -1.99999999998764832476e-01;
+ private static final double AT2 = 1.42857142725034663711e-01;
+ private static final double AT3 = -1.11111104054623557880e-01;
+ private static final double AT4 = 9.09088713343650656196e-02;
+ private static final double AT5 = -7.69187620504482999495e-02;
+ private static final double AT6 = 6.66107313738753120669e-02;
+ private static final double AT7= -5.83357013379057348645e-02;
+ private static final double AT8 = 4.97687799461593236017e-02;
+ private static final double AT9 = -3.65315727442169155270e-02;
+ private static final double AT10 = 1.62858201153657823623e-02;
/**
* Returns the closest double approximation of the arc tangent of the
@@ -149,11 +291,73 @@ public final class StrictMath {
* <li>{@code atan(NaN) = NaN}</li>
* </ul>
*
- * @param d
+ * @param x
* the value whose arc tangent has to be computed.
* @return the arc tangent of the argument.
*/
- public static native double atan(double d);
+ public static double atan(double x) {
+ double w, s1, s2, z;
+ int ix, hx, id;
+
+ final long bits = Double.doubleToRawLongBits(x);
+ hx = (int) (bits >>> 32);
+ ix = hx & 0x7fffffff;
+ if (ix >= 0x44100000) { /* if |x| >= 2^66 */
+ if (ix > 0x7ff00000 || (ix == 0x7ff00000 && (((int) bits) != 0))) {
+ return x + x; /* NaN */
+ }
+ if (hx > 0) {
+ return ATANHI[3] + ATANLO[3];
+ } else {
+ return -ATANHI[3] - ATANLO[3];
+ }
+ }
+ if (ix < 0x3fdc0000) { /* |x| < 0.4375 */
+ if (ix < 0x3e200000) { /* |x| < 2^-29 */
+ if (HUGE + x > 1.00000000000000000000e+00) {
+ return x; /* raise inexact */
+ }
+ }
+ id = -1;
+ } else {
+ x = Math.abs(x);
+ if (ix < 0x3ff30000) { /* |x| < 1.1875 */
+ if (ix < 0x3fe60000) { /* 7/16 <=|x|<11/16 */
+ id = 0;
+ x = (2.0 * x - 1.00000000000000000000e+00) / (2.0 + x);
+ } else { /* 11/16<=|x|< 19/16 */
+ id = 1;
+ x = (x - 1.00000000000000000000e+00) / (x + 1.00000000000000000000e+00);
+ }
+ } else {
+ if (ix < 0x40038000) { /* |x| < 2.4375 */
+ id = 2;
+ x = (x - 1.5) / (1.00000000000000000000e+00 + 1.5 * x);
+ } else { /* 2.4375 <= |x| < 2^66 */
+ id = 3;
+ x = -1.0 / x;
+ }
+ }
+ }
+
+ /* end of argument reduction */
+ z = x * x;
+ w = z * z;
+ /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */
+ s1 = z * (AT0 + w * (AT2 + w
+ * (AT4 + w * (AT6 + w * (AT8 + w * AT10)))));
+ s2 = w * (AT1 + w * (AT3 + w * (AT5 + w * (AT7 + w * AT9))));
+ if (id < 0) {
+ return x - x * (s1 + s2);
+ } else {
+ z = ATANHI[id] - ((x * (s1 + s2) - ATANLO[id]) - x);
+ return (hx < 0) ? -z : z;
+ }
+ }
+
+ private static final double PI_O_4 = 7.8539816339744827900E-01;
+ private static final double PI_O_2 = 1.5707963267948965580E+00;
+ private static final double PI_LO = 1.2246467991473531772E-16;
/**
* Returns the closest double approximation of the arc tangent of
@@ -192,7 +396,108 @@ public final class StrictMath {
* the denominator of the value whose atan has to be computed.
* @return the arc tangent of {@code y/x}.
*/
- public static native double atan2(double y, double x);
+ public static double atan2(double y, double x) {
+ double z;
+ int k, m, hx, hy, ix, iy;
+ int lx, ly; // watch out, should be unsigned
+
+ final long yBits = Double.doubleToRawLongBits(y);
+ final long xBits = Double.doubleToRawLongBits(x);
+
+ hx = (int) (xBits >>> 32); // __HI(x);
+ ix = hx & 0x7fffffff;
+ lx = (int) xBits; // __LO(x);
+ hy = (int) (yBits >>> 32); // __HI(y);
+ iy = hy & 0x7fffffff;
+ ly = (int) yBits; // __LO(y);
+ if (((ix | ((lx | -lx) >> 31)) > 0x7ff00000)
+ || ((iy | ((ly | -ly) >> 31)) > 0x7ff00000)) { /* x or y is NaN */
+ return x + y;
+ }
+ if ((hx - 0x3ff00000 | lx) == 0) {
+ return StrictMath.atan(y); /* x=1.0 */
+ }
+
+ m = ((hy >> 31) & 1) | ((hx >> 30) & 2); /* 2*sign(x)+sign(y) */
+
+ /* when y = 0 */
+ if ((iy | ly) == 0) {
+ switch (m) {
+ case 0:
+ case 1:
+ return y; /* ieee_atan(+-0,+anything)=+-0 */
+ case 2:
+ return 3.14159265358979311600e+00 + TINY;/* ieee_atan(+0,-anything) = pi */
+ case 3:
+ return -3.14159265358979311600e+00 - TINY;/* ieee_atan(-0,-anything) =-pi */
+ }
+ }
+ /* when x = 0 */
+ if ((ix | lx) == 0)
+ return (hy < 0) ? -PI_O_2 - TINY : PI_O_2 + TINY;
+
+ /* when x is INF */
+ if (ix == 0x7ff00000) {
+ if (iy == 0x7ff00000) {
+ switch (m) {
+ case 0:
+ return PI_O_4 + TINY;/* ieee_atan(+INF,+INF) */
+ case 1:
+ return -PI_O_4 - TINY;/* ieee_atan(-INF,+INF) */
+ case 2:
+ return 3.0 * PI_O_4 + TINY;/* ieee_atan(+INF,-INF) */
+ case 3:
+ return -3.0 * PI_O_4 - TINY;/* ieee_atan(-INF,-INF) */
+ }
+ } else {
+ switch (m) {
+ case 0:
+ return 0.0; /* ieee_atan(+...,+INF) */
+ case 1:
+ return -0.0; /* ieee_atan(-...,+INF) */
+ case 2:
+ return 3.14159265358979311600e+00 + TINY; /* ieee_atan(+...,-INF) */
+ case 3:
+ return -3.14159265358979311600e+00 - TINY; /* ieee_atan(-...,-INF) */
+ }
+ }
+ }
+ /* when y is INF */
+ if (iy == 0x7ff00000)
+ return (hy < 0) ? -PI_O_2 - TINY : PI_O_2 + TINY;
+
+ /* compute y/x */
+ k = (iy - ix) >> 20;
+ if (k > 60) {
+ z = PI_O_2 + 0.5 * PI_LO; /* |y/x| > 2**60 */
+ } else if (hx < 0 && k < -60) {
+ z = 0.0; /* |y|/x < -2**60 */
+ } else {
+ z = StrictMath.atan(Math.abs(y / x)); /* safe to do y/x */
+ }
+
+ switch (m) {
+ case 0:
+ return z; /* ieee_atan(+,+) */
+ case 1:
+ // __HI(z) ^= 0x80000000;
+ z = Double.longBitsToDouble(
+ Double.doubleToRawLongBits(z) ^ (0x80000000L << 32));
+ return z; /* ieee_atan(-,+) */
+ case 2:
+ return 3.14159265358979311600e+00 - (z - PI_LO);/* ieee_atan(+,-) */
+ default: /* case 3 */
+ return (z - PI_LO) - 3.14159265358979311600e+00;/* ieee_atan(-,-) */
+ }
+ }
+
+ private static final int B1 = 715094163;
+ private static final int B2 = 696219795;
+ private static final double C = 5.42857142857142815906e-01;
+ private static final double D = -7.05306122448979611050e-01;
+ private static final double CBRTE = 1.41428571428571436819e+00;
+ private static final double F = 1.60714285714285720630e+00;
+ private static final double G = 3.57142857142857150787e-01;
/**
* Returns the closest double approximation of the cube root of the
@@ -207,11 +512,79 @@ public final class StrictMath {
* <li>{@code cbrt(NaN) = NaN}</li>
* </ul>
*
- * @param d
+ * @param x
* the value whose cube root has to be computed.
* @return the cube root of the argument.
*/
- public static native double cbrt(double d);
+ public static double cbrt(double x) {
+ if (x < 0) {
+ return -cbrt(-x);
+ }
+ int hx;
+ double r, s, w;
+ int sign; // caution: should be unsigned
+ long bits = Double.doubleToRawLongBits(x);
+
+ hx = (int) (bits >>> 32);
+ sign = hx & 0x80000000; /* sign= sign(x) */
+ hx ^= sign;
+ if (hx >= 0x7ff00000) {
+ return (x + x); /* ieee_cbrt(NaN,INF) is itself */
+ }
+
+ if ((hx | ((int) bits)) == 0) {
+ return x; /* ieee_cbrt(0) is itself */
+ }
+
+ // __HI(x) = hx; /* x <- |x| */
+ bits &= 0x00000000ffffffffL;
+ bits |= ((long) hx << 32);
+
+ long tBits = Double.doubleToRawLongBits(0.0) & 0x00000000ffffffffL;
+ double t = 0.0;
+ /* rough cbrt to 5 bits */
+ if (hx < 0x00100000) { /* subnormal number */
+ // __HI(t)=0x43500000; /*set t= 2**54*/
+ tBits |= 0x43500000L << 32;
+ t = Double.longBitsToDouble(tBits);
+ t *= x;
+
+ // __HI(t)=__HI(t)/3+B2;
+ tBits = Double.doubleToRawLongBits(t);
+ long tBitsHigh = tBits >> 32;
+ tBits &= 0x00000000ffffffffL;
+ tBits |= ((tBitsHigh / 3) + B2) << 32;
+ t = Double.longBitsToDouble(tBits);
+
+ } else {
+ // __HI(t)=hx/3+B1;
+ tBits |= ((long) ((hx / 3) + B1)) << 32;
+ t = Double.longBitsToDouble(tBits);
+ }
+
+ /* new cbrt to 23 bits, may be implemented in single precision */
+ r = t * t / x;
+ s = C + r * t;
+ t *= G + F / (s + CBRTE + D / s);
+
+ /* chopped to 20 bits and make it larger than ieee_cbrt(x) */
+ tBits = Double.doubleToRawLongBits(t);
+ tBits &= 0xFFFFFFFFL << 32;
+ tBits += 0x00000001L << 32;
+ t = Double.longBitsToDouble(tBits);
+
+ /* one step newton iteration to 53 bits with error less than 0.667 ulps */
+ s = t * t; /* t*t is exact */
+ r = x / s;
+ w = t + t;
+ r = (r - t) / (w + r); /* r-s is exact */
+ t = t + t * r;
+
+ /* retore the sign bit */
+ tBits = Double.doubleToRawLongBits(t);
+ tBits |= ((long) sign) << 32;
+ return Double.longBitsToDouble(tBits);
+ }
/**
* Returns the double conversion of the most negative (closest to negative
@@ -229,6 +602,8 @@ public final class StrictMath {
*/
public static native double ceil(double d);
+ private static final long ONEBITS = Double.doubleToRawLongBits(1.00000000000000000000e+00)
+ & 0x00000000ffffffffL;
/**
* Returns the closest double approximation of the hyperbolic cosine of the
@@ -241,11 +616,54 @@ public final class StrictMath {
* <li>{@code cosh(NaN) = NaN}</li>
* </ul>
*
- * @param d
+ * @param x
* the value whose hyperbolic cosine has to be computed.
* @return the hyperbolic cosine of the argument.
*/
- public static native double cosh(double d);
+ public static double cosh(double x) {
+ double t, w;
+ int ix;
+ final long bits = Double.doubleToRawLongBits(x);
+ ix = (int) (bits >>> 32) & 0x7fffffff;
+
+ /* x is INF or NaN */
+ if (ix >= 0x7ff00000) {
+ return x * x;
+ }
+
+ /* |x| in [0,0.5*ln2], return 1+ieee_expm1(|x|)^2/(2*ieee_exp(|x|)) */
+ if (ix < 0x3fd62e43) {
+ t = expm1(Math.abs(x));
+ w = 1.00000000000000000000e+00 + t;
+ if (ix < 0x3c800000)
+ return w; /* ieee_cosh(tiny) = 1 */
+ return 1.00000000000000000000e+00 + (t * t) / (w + w);
+ }
+
+ /* |x| in [0.5*ln2,22], return (ieee_exp(|x|)+1/ieee_exp(|x|)/2; */
+ if (ix < 0x40360000) {
+ t = exp(Math.abs(x));
+ return 0.5 * t + 0.5 / t;
+ }
+
+ /* |x| in [22, ieee_log(maxdouble)] return half*ieee_exp(|x|) */
+ if (ix < 0x40862E42) {
+ return 0.5 * exp(Math.abs(x));
+ }
+
+ /* |x| in [log(maxdouble), overflowthresold] */
+ final long lx = ((ONEBITS >>> 29) + ((int) bits)) & 0x00000000ffffffffL;
+ // watch out: lx should be an unsigned int
+ // lx = *( (((*(unsigned*)&one)>>29)) + (unsigned*)&x);
+ if (ix < 0x408633CE || (ix == 0x408633ce) && (lx <= 0x8fb9f87dL)) {
+ w = exp(0.5 * Math.abs(x));
+ t = 0.5 * w;
+ return t * w;
+ }
+
+ /* |x| > overflowthresold, ieee_cosh(x) overflow */
+ return HUGE * HUGE;
+ }
/**
* Returns the closest double approximation of the cosine of the argument.
@@ -263,6 +681,19 @@ public final class StrictMath {
*/
public static native double cos(double d);
+ private static final double TWON24 = 5.96046447753906250000e-08;
+ private static final double TWO54 = 1.80143985094819840000e+16,
+ TWOM54 = 5.55111512312578270212e-17;
+ private static final double TWOM1000 = 9.33263618503218878990e-302;
+ private static final double O_THRESHOLD = 7.09782712893383973096e+02;
+ private static final double U_THRESHOLD = -7.45133219101941108420e+02;
+ private static final double INVLN2 = 1.44269504088896338700e+00;
+ private static final double P1 = 1.66666666666666019037e-01;
+ private static final double P2 = -2.77777777770155933842e-03;
+ private static final double P3 = 6.61375632143793436117e-05;
+ private static final double P4 = -1.65339022054652515390e-06;
+ private static final double P5 = 4.13813679705723846039e-08;
+
/**
* Returns the closest double approximation of the raising "e" to the power
* of the argument.
@@ -274,11 +705,88 @@ public final class StrictMath {
* <li>{@code exp(NaN) = NaN}</li>
* </ul>
*
- * @param d
+ * @param x
* the value whose exponential has to be computed.
* @return the exponential of the argument.
*/
- public static native double exp(double d);
+ public static double exp(double x) {
+ double y, c, t;
+ double hi = 0, lo = 0;
+ int k = 0, xsb;
+ int hx; // should be unsigned, be careful!
+ final long bits = Double.doubleToRawLongBits(x);
+ int lowBits = (int) bits;
+ int highBits = (int) (bits >>> 32);
+ hx = highBits & 0x7fffffff;
+ xsb = (highBits >>> 31) & 1;
+
+ /* filter out non-finite argument */
+ if (hx >= 0x40862E42) { /* if |x|>=709.78... */
+ if (hx >= 0x7ff00000) {
+ if (((hx & 0xfffff) | lowBits) != 0) {
+ return x + x; /* NaN */
+ } else {
+ return (xsb == 0) ? x : 0.0; /* ieee_exp(+-inf)={inf,0} */
+ }
+ }
+
+ if (x > O_THRESHOLD) {
+ return HUGE * HUGE; /* overflow */
+ }
+
+ if (x < U_THRESHOLD) {
+ return TWOM1000 * TWOM1000; /* underflow */
+ }
+ }
+
+ /* argument reduction */
+ if (hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */
+ if (hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */
+ hi = x - ((xsb == 0) ? 6.93147180369123816490e-01 :
+ -6.93147180369123816490e-01); // LN2HI[xsb];
+ lo = (xsb == 0) ? 1.90821492927058770002e-10 :
+ -1.90821492927058770002e-10; // LN2LO[xsb];
+ k = 1 - xsb - xsb;
+ } else {
+ k = (int) (INVLN2 * x + ((xsb == 0) ? 0.5 : -0.5 ));//halF[xsb]);
+ t = k;
+ hi = x - t * 6.93147180369123816490e-01; //ln2HI[0]; /* t*ln2HI is exact here */
+ lo = t * 1.90821492927058770002e-10; //ln2LO[0];
+ }
+ x = hi - lo;
+ } else if (hx < 0x3e300000) { /* when |x|<2**-28 */
+ if (HUGE + x > 1.00000000000000000000e+00)
+ return 1.00000000000000000000e+00 + x;/* trigger inexact */
+ } else {
+ k = 0;
+ }
+
+ /* x is now in primary range */
+ t = x * x;
+ c = x - t * (P1 + t * (P2 + t * (P3 + t * (P4 + t * P5))));
+ if (k == 0) {
+ return 1.00000000000000000000e+00 - ((x * c) / (c - 2.0) - x);
+ } else {
+ y = 1.00000000000000000000e+00 - ((lo - (x * c) / (2.0 - c)) - hi);
+ }
+ long yBits = Double.doubleToRawLongBits(y);
+ if (k >= -1021) {
+ yBits += ((long) (k << 20)) << 32; /* add k to y's exponent */
+ return Double.longBitsToDouble(yBits);
+ } else {
+ yBits += ((long) ((k + 1000) << 20)) << 32;/* add k to y's exponent */
+ return Double.longBitsToDouble(yBits) * TWOM1000;
+ }
+ }
+
+ private static final double TINY = 1.0e-300;
+ private static final double LN2_HI = 6.93147180369123816490e-01;
+ private static final double LN2_LO = 1.90821492927058770002e-10;
+ private static final double Q1 = -3.33333333333331316428e-02;
+ private static final double Q2 = 1.58730158725481460165e-03;
+ private static final double Q3 = -7.93650757867487942473e-05;
+ private static final double Q4 = 4.00821782732936239552e-06;
+ private static final double Q5 = -2.01099218183624371326e-07;
/**
* Returns the closest double approximation of <i>{@code e}</i><sup>
@@ -295,17 +803,124 @@ public final class StrictMath {
* <li>{@code expm1(NaN) = NaN}</li>
* </ul>
*
- * @param d
+ * @param x
* the value to compute the <i>{@code e}</i><sup>{@code d}</sup>
* {@code - 1} of.
- * @return the <i>{@code e}</i><sup>{@code d}</sup>{@code - 1} value
- * of the argument.
+ * @return the <i>{@code e}</i><sup>{@code d}</sup>{@code - 1} value of the
+ * argument.
*/
- public static native double expm1(double d);
+ public static double expm1(double x) {
+ double y, hi, lo, t, e, hxs, hfx, r1, c = 0.0;
+ int k, xsb;
+ long yBits = 0;
+ final long bits = Double.doubleToRawLongBits(x);
+ int highBits = (int) (bits >>> 32);
+ int lowBits = (int) (bits);
+ int hx = highBits & 0x7fffffff; // caution: should be unsigned!
+ xsb = highBits & 0x80000000; /* sign bit of x */
+ y = xsb == 0 ? x : -x; /* y = |x| */
+
+ /* filter out huge and non-finite argument */
+ if (hx >= 0x4043687A) { /* if |x|>=56*ln2 */
+ if (hx >= 0x40862E42) { /* if |x|>=709.78... */
+ if (hx >= 0x7ff00000) {
+ if (((hx & 0xfffff) | lowBits) != 0) {
+ return x + x; /* NaN */
+ } else {
+ return (xsb == 0) ? x : -1.0;/* ieee_exp(+-inf)={inf,-1} */
+ }
+ }
+ if (x > O_THRESHOLD) {
+ return HUGE * HUGE; /* overflow */
+ }
+ }
+ if (xsb != 0) { /* x < -56*ln2, return -1.0 with inexact */
+ if (x + TINY < 0.0) { /* raise inexact */
+ return TINY - 1.00000000000000000000e+00; /* return -1 */
+ }
+ }
+ }
+ /* argument reduction */
+ if (hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */
+ if (hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */
+ if (xsb == 0) {
+ hi = x - LN2_HI;
+ lo = LN2_LO;
+ k = 1;
+ } else {
+ hi = x + LN2_HI;
+ lo = -LN2_LO;
+ k = -1;
+ }
+ } else {
+ k = (int) (INVLN2 * x + ((xsb == 0) ? 0.5 : -0.5));
+ t = k;
+ hi = x - t * LN2_HI; /* t*ln2_hi is exact here */
+ lo = t * LN2_LO;
+ }
+ x = hi - lo;
+ c = (hi - x) - lo;
+ } else if (hx < 0x3c900000) { /* when |x|<2**-54, return x */
+ // t = huge+x; /* return x with inexact flags when x!=0 */
+ // return x - (t-(huge+x));
+ return x; // inexact flag is not set, but Java ignors this flag
+ // anyway
+ } else {
+ k = 0;
+ }
+
+ /* x is now in primary range */
+ hfx = 0.5 * x;
+ hxs = x * hfx;
+ r1 = 1.00000000000000000000e+00 + hxs * (Q1 + hxs * (Q2 + hxs * (Q3 + hxs * (Q4 + hxs * Q5))));
+ t = 3.0 - r1 * hfx;
+ e = hxs * ((r1 - t) / (6.0 - x * t));
+ if (k == 0) {
+ return x - (x * e - hxs); /* c is 0 */
+ } else {
+ e = (x * (e - c) - c);
+ e -= hxs;
+ if (k == -1) {
+ return 0.5 * (x - e) - 0.5;
+ }
+
+ if (k == 1) {
+ if (x < -0.25) {
+ return -2.0 * (e - (x + 0.5));
+ } else {
+ return 1.00000000000000000000e+00 + 2.0 * (x - e);
+ }
+ }
+
+ if (k <= -2 || k > 56) { /* suffice to return ieee_exp(x)-1 */
+ y = 1.00000000000000000000e+00 - (e - x);
+ yBits = Double.doubleToRawLongBits(y);
+ yBits += (((long) k) << 52); /* add k to y's exponent */
+ return Double.longBitsToDouble(yBits) - 1.00000000000000000000e+00;
+ }
+
+ long tBits = Double.doubleToRawLongBits(1.00000000000000000000e+00) & 0x00000000ffffffffL;
+
+ if (k < 20) {
+ tBits |= (((long) 0x3ff00000) - (0x200000 >> k)) << 32;
+ y = Double.longBitsToDouble(tBits) - (e - x);
+ yBits = Double.doubleToRawLongBits(y);
+ yBits += (((long) k) << 52); /* add k to y's exponent */
+ return Double.longBitsToDouble(yBits);
+ } else {
+ tBits |= ((((long) 0x3ff) - k) << 52); /* 2^-k */
+ y = x - (e + Double.longBitsToDouble(tBits));
+ y += 1.00000000000000000000e+00;
+ yBits = Double.doubleToRawLongBits(y);
+ yBits += (((long) k) << 52); /* add k to y's exponent */
+ return Double.longBitsToDouble(yBits);
+ }
+ }
+ }
/**
- * Returns the double conversion of the most positive (closest to
- * positive infinity) integer less than or equal to the argument.
+ * Returns the double conversion of the most positive (closest to positive
+ * infinity) integer less than or equal to the argument.
* <p>
* Special cases:
* <ul>
@@ -319,9 +934,9 @@ public final class StrictMath {
public static native double floor(double d);
/**
- * Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +}
- * <i> {@code y}</i><sup>{@code 2}</sup>{@code )}. The final result is
- * without medium underflow or overflow.
+ * Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +} <i>
+ * {@code y}</i><sup>{@code 2}</sup>{@code )}. The final result is without
+ * medium underflow or overflow.
* <p>
* Special cases:
* <ul>
@@ -369,6 +984,14 @@ public final class StrictMath {
*/
public static native double IEEEremainder(double x, double y);
+ private static final double LG1 = 6.666666666666735130e-01;
+ private static final double LG2 = 3.999999999940941908e-01;
+ private static final double LG3 = 2.857142874366239149e-01;
+ private static final double LG4 = 2.222219843214978396e-01;
+ private static final double LG5 = 1.818357216161805012e-01;
+ private static final double LG6 = 1.531383769920937332e-01;
+ private static final double LG7 = 1.479819860511658591e-01;
+
/**
* Returns the closest double approximation of the natural logarithm of the
* argument.
@@ -383,11 +1006,95 @@ public final class StrictMath {
* <li>{@code log(NaN) = NaN}</li>
* </ul>
*
- * @param d
+ * @param x
* the value whose log has to be computed.
* @return the natural logarithm of the argument.
*/
- public static native double log(double d);
+ public static double log(double x) {
+ double hfsq, f, s, z, R, w, t1, t2, dk;
+ int hx, i, j, k = 0;
+ int lx; // watch out, should be unsigned
+
+ long bits = Double.doubleToRawLongBits(x);
+ hx = (int) (bits >>> 32); /* high word of x */
+ lx = (int) bits; /* low word of x */
+
+ if (hx < 0x00100000) { /* x < 2**-1022 */
+ if (((hx & 0x7fffffff) | lx) == 0) {
+ return -TWO54 / 0.0; /* ieee_log(+-0)=-inf */
+ }
+
+ if (hx < 0) {
+ return (x - x) / 0.0; /* ieee_log(-#) = NaN */
+ }
+
+ k -= 54;
+ x *= TWO54; /* subnormal number, scale up x */
+ bits = Double.doubleToRawLongBits(x);
+ hx = (int) (bits >>> 32); /* high word of x */
+ }
+
+ if (hx >= 0x7ff00000) {
+ return x + x;
+ }
+
+ k += (hx >> 20) - 1023;
+ hx &= 0x000fffff;
+ bits &= 0x00000000ffffffffL;
+ i = (hx + 0x95f64) & 0x100000;
+ bits |= ((long) hx | (i ^ 0x3ff00000)) << 32; /* normalize x or x/2 */
+ x = Double.longBitsToDouble(bits);
+ k += (i >> 20);
+ f = x - 1.0;
+
+ if ((0x000fffff & (2 + hx)) < 3) { /* |f| < 2**-20 */
+ if (f == 0.0) {
+ if (k == 0) {
+ return 0.0;
+ } else {
+ dk = k;
+ }
+ return dk * LN2_HI + dk * LN2_LO;
+ }
+
+ R = f * f * (0.5 - 0.33333333333333333 * f);
+ if (k == 0) {
+ return f - R;
+ } else {
+ dk = k;
+ return dk * LN2_HI - ((R - dk * LN2_LO) - f);
+ }
+ }
+ s = f / (2.0 + f);
+ dk = k;
+ z = s * s;
+ i = hx - 0x6147a;
+ w = z * z;
+ j = 0x6b851 - hx;
+ t1 = w * (LG2 + w * (LG4 + w * LG6));
+ t2 = z * (LG1 + w * (LG3 + w * (LG5 + w * LG7)));
+ i |= j;
+ R = t2 + t1;
+ if (i > 0) {
+ hfsq = 0.5 * f * f;
+ if (k == 0) {
+ return f - (hfsq - s * (hfsq + R));
+ } else {
+ return dk * LN2_HI
+ - ((hfsq - (s * (hfsq + R) + dk * LN2_LO)) - f);
+ }
+ } else {
+ if (k == 0) {
+ return f - s * (f - R);
+ } else {
+ return dk * LN2_HI - ((s * (f - R) - dk * LN2_LO) - f);
+ }
+ }
+ }
+
+ private static final double IVLN10 = 4.34294481903251816668e-01;
+ private static final double LOG10_2HI = 3.01029995663611771306e-01;
+ private static final double LOG10_2LO = 3.69423907715893078616e-13;
/**
* Returns the closest double approximation of the base 10 logarithm of the
@@ -403,11 +1110,54 @@ public final class StrictMath {
* <li>{@code log10(NaN) = NaN}</li>
* </ul>
*
- * @param d
+ * @param x
* the value whose base 10 log has to be computed.
- * @return the natural logarithm of the argument.
+ * @return the the base 10 logarithm of x
*/
- public static native double log10(double d);
+ public static double log10(double x) {
+ double y, z;
+ int i, k = 0, hx;
+ int lx; // careful: lx should be unsigned!
+ long bits = Double.doubleToRawLongBits(x);
+ hx = (int) (bits >> 32); /* high word of x */
+ lx = (int) bits; /* low word of x */
+ if (hx < 0x00100000) { /* x < 2**-1022 */
+ if (((hx & 0x7fffffff) | lx) == 0) {
+ return -TWO54 / 0.0; /* ieee_log(+-0)=-inf */
+ }
+
+ if (hx < 0) {
+ return (x - x) / 0.0; /* ieee_log(-#) = NaN */
+ }
+
+ k -= 54;
+ x *= TWO54; /* subnormal number, scale up x */
+ bits = Double.doubleToRawLongBits(x);
+ hx = (int) (bits >> 32); /* high word of x */
+ }
+
+ if (hx >= 0x7ff00000) {
+ return x + x;
+ }
+
+ k += (hx >> 20) - 1023;
+ i = (int) (((k & 0x00000000ffffffffL) & 0x80000000) >>> 31);
+ hx = (hx & 0x000fffff) | ((0x3ff - i) << 20);
+ y = k + i;
+ bits &= 0x00000000ffffffffL;
+ bits |= ((long) hx) << 32;
+ x = Double.longBitsToDouble(bits); // __HI(x) = hx;
+ z = y * LOG10_2LO + IVLN10 * log(x);
+ return z + y * LOG10_2HI;
+ }
+
+ private static final double LP1 = 6.666666666666735130e-01,
+ LP2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */
+ LP3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */
+ LP4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */
+ LP5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */
+ LP6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */
+ LP7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */
/**
* Returns the closest double approximation of the natural logarithm of the
@@ -426,11 +1176,107 @@ public final class StrictMath {
* <li>{@code log1p(NaN) = NaN}</li>
* </ul>
*
- * @param d
+ * @param x
* the value to compute the {@code ln(1+d)} of.
* @return the natural logarithm of the sum of the argument and 1.
*/
- public static native double log1p(double d);
+
+ public static double log1p(double x) {
+ double hfsq, f = 0.0, c = 0.0, s, z, R, u = 0.0;
+ int k, hx, hu = 0, ax;
+
+ final long bits = Double.doubleToRawLongBits(x);
+ hx = (int) (bits >>> 32); /* high word of x */
+ ax = hx & 0x7fffffff;
+
+ k = 1;
+ if (hx < 0x3FDA827A) { /* x < 0.41422 */
+ if (ax >= 0x3ff00000) { /* x <= -1.0 */
+ if (x == -1.0) {
+ return -TWO54 / 0.0; /* ieee_log1p(-1)=+inf */
+ } else {
+ return (x - x) / (x - x); /* ieee_log1p(x<-1)=NaN */
+ }
+ }
+ if (ax < 0x3e200000) {
+ if (TWO54 + x > 0.0 && ax < 0x3c900000) {
+ return x;
+ } else {
+ return x - x * x * 0.5;
+ }
+ }
+ if (hx > 0 || hx <= 0xbfd2bec3) {
+ k = 0;
+ f = x;
+ hu = 1;
+ } /* -0.2929<x<0.41422 */
+ }
+
+ if (hx >= 0x7ff00000) {
+ return x + x;
+ }
+
+ if (k != 0) {
+ long uBits;
+ if (hx < 0x43400000) {
+ u = 1.0 + x;
+ uBits = Double.doubleToRawLongBits(u);
+ hu = (int) (uBits >>> 32);
+ k = (hu >> 20) - 1023;
+ c = (k > 0) ? 1.0 - (u - x) : x - (u - 1.0);/* correction term */
+ c /= u;
+ } else {
+ uBits = Double.doubleToRawLongBits(x);
+ hu = (int) (uBits >>> 32);
+ k = (hu >> 20) - 1023;
+ c = 0;
+ }
+ hu &= 0x000fffff;
+ if (hu < 0x6a09e) {
+ // __HI(u) = hu|0x3ff00000; /* normalize u */
+ uBits &= 0x00000000ffffffffL;
+ uBits |= ((long) hu | 0x3ff00000) << 32;
+ u = Double.longBitsToDouble(uBits);
+ } else {
+ k += 1;
+ // __HI(u) = hu|0x3fe00000; /* normalize u/2 */
+ uBits &= 0xffffffffL;
+ uBits |= ((long) hu | 0x3fe00000) << 32;
+ u = Double.longBitsToDouble(uBits);
+ hu = (0x00100000 - hu) >> 2;
+ }
+ f = u - 1.0;
+ }
+ hfsq = 0.5 * f * f;
+ if (hu == 0) { /* |f| < 2**-20 */
+ if (f == 0.0) {
+ if (k == 0) {
+ return 0.0;
+ } else {
+ c += k * LN2_LO;
+ return k * LN2_HI + c;
+ }
+ }
+
+ R = hfsq * (1.0 - 0.66666666666666666 * f);
+ if (k == 0) {
+ return f - R;
+ } else {
+ return k * LN2_HI - ((R - (k * LN2_LO + c)) - f);
+ }
+ }
+
+ s = f / (2.0 + f);
+ z = s * s;
+ R = z * (LP1 + z * (LP2 + z
+ * (LP3 + z * (LP4 + z * (LP5 + z * (LP6 + z * LP7))))));
+ if (k == 0) {
+ return f - (hfsq - s * (hfsq + R));
+ } else {
+ return k * LN2_HI
+ - ((hfsq - (s * (hfsq + R) + (k * LN2_LO + c))) - f);
+ }
+ }
/**
* Returns the most positive (closest to positive infinity) of the two
@@ -453,8 +1299,8 @@ public final class StrictMath {
if (d1 != d2)
return Double.NaN;
/* max( +0.0,-0.0) == +0.0 */
- if (d1 == 0.0
- && ((Double.doubleToLongBits(d1) & Double.doubleToLongBits(d2)) & 0x8000000000000000L) == 0)
+ if (d1 == 0.0 &&
+ ((Double.doubleToLongBits(d1) & Double.doubleToLongBits(d2)) & 0x8000000000000000L) == 0)
return 0.0;
return d1;
}
@@ -480,8 +1326,8 @@ public final class StrictMath {
if (f1 != f2)
return Float.NaN;
/* max( +0.0,-0.0) == +0.0 */
- if (f1 == 0.0f
- && ((Float.floatToIntBits(f1) & Float.floatToIntBits(f2)) & 0x80000000) == 0)
+ if (f1 == 0.0f &&
+ ((Float.floatToIntBits(f1) & Float.floatToIntBits(f2)) & 0x80000000) == 0)
return 0.0f;
return f1;
}
@@ -523,8 +1369,8 @@ public final class StrictMath {
if (d1 != d2)
return Double.NaN;
/* min( +0.0,-0.0) == -0.0 */
- if (d1 == 0.0
- && ((Double.doubleToLongBits(d1) | Double.doubleToLongBits(d2)) & 0x8000000000000000l) != 0)
+ if (d1 == 0.0 &&
+ ((Double.doubleToLongBits(d1) | Double.doubleToLongBits(d2)) & 0x8000000000000000l) != 0)
return 0.0 * (-1.0);
return d1;
}
@@ -550,8 +1396,8 @@ public final class StrictMath {
if (f1 != f2)
return Float.NaN;
/* min( +0.0,-0.0) == -0.0 */
- if (f1 == 0.0f
- && ((Float.floatToIntBits(f1) | Float.floatToIntBits(f2)) & 0x80000000) != 0)
+ if (f1 == 0.0f &&
+ ((Float.floatToIntBits(f1) | Float.floatToIntBits(f2)) & 0x80000000) != 0)
return 0.0f * (-1.0f);
return f1;
}
@@ -706,7 +1552,7 @@ public final class StrictMath {
* the value whose signum has to be computed.
* @return the value of the signum function.
*/
- public static double signum(double d){
+ public static double signum(double d) {
return Math.signum(d);
}
@@ -729,10 +1575,12 @@ public final class StrictMath {
* the value whose signum has to be computed.
* @return the value of the signum function.
*/
- public static float signum(float f){
+ public static float signum(float f) {
return Math.signum(f);
}
+ private static final double shuge = 1.0e307;
+
/**
* Returns the closest double approximation of the hyperbolic sine of the
* argument.
@@ -746,11 +1594,57 @@ public final class StrictMath {
* <li>{@code sinh(NaN) = NaN}</li>
* </ul>
*
- * @param d
+ * @param x
* the value whose hyperbolic sine has to be computed.
* @return the hyperbolic sine of the argument.
*/
- public static native double sinh(double d);
+ public static double sinh(double x) {
+ double t, w, h;
+ int ix, jx;
+ final long bits = Double.doubleToRawLongBits(x);
+
+ jx = (int) (bits >>> 32);
+ ix = jx & 0x7fffffff;
+
+ /* x is INF or NaN */
+ if (ix >= 0x7ff00000) {
+ return x + x;
+ }
+
+ h = 0.5;
+ if (jx < 0) {
+ h = -h;
+ }
+
+ /* |x| in [0,22], return sign(x)*0.5*(E+E/(E+1))) */
+ if (ix < 0x40360000) { /* |x|<22 */
+ if (ix < 0x3e300000) /* |x|<2**-28 */
+ if (shuge + x > 1.00000000000000000000e+00) {
+ return x;/* ieee_sinh(tiny) = tiny with inexact */
+ }
+ t = expm1(Math.abs(x));
+ if (ix < 0x3ff00000)
+ return h * (2.0 * t - t * t / (t + 1.00000000000000000000e+00));
+ return h * (t + t / (t + 1.00000000000000000000e+00));
+ }
+
+ /* |x| in [22, ieee_log(maxdouble)] return 0.5*ieee_exp(|x|) */
+ if (ix < 0x40862E42) {
+ return h * exp(Math.abs(x));
+ }
+
+ /* |x| in [log(maxdouble), overflowthresold] */
+ final long lx = ((ONEBITS >>> 29) + ((int) bits)) & 0x00000000ffffffffL;
+ // lx = *( (((*(unsigned*)&one)>>29)) + (unsigned*)&x);
+ if (ix < 0x408633CE || (ix == 0x408633ce) && (lx <= 0x8fb9f87dL)) {
+ w = exp(0.5 * Math.abs(x));
+ t = h * w;
+ return t * w;
+ }
+
+ /* |x| > overflowthresold, ieee_sinh(x) overflow */
+ return x * shuge;
+ }
/**
* Returns the closest double approximation of the sine of the argument.
@@ -816,11 +1710,47 @@ public final class StrictMath {
* <li>{@code tanh(NaN) = NaN}</li>
* </ul>
*
- * @param d
+ * @param x
* the value whose hyperbolic tangent has to be computed.
* @return the hyperbolic tangent of the argument
*/
- public static native double tanh(double d);
+ public static double tanh(double x) {
+ double t, z;
+ int jx, ix;
+
+ final long bits = Double.doubleToRawLongBits(x);
+ /* High word of |x|. */
+ jx = (int) (bits >>> 32);
+ ix = jx & 0x7fffffff;
+
+ /* x is INF or NaN */
+ if (ix >= 0x7ff00000) {
+ if (jx >= 0) {
+ return 1.00000000000000000000e+00 / x + 1.00000000000000000000e+00; /* ieee_tanh(+-inf)=+-1 */
+ } else {
+ return 1.00000000000000000000e+00 / x - 1.00000000000000000000e+00; /* ieee_tanh(NaN) = NaN */
+ }
+ }
+
+ /* |x| < 22 */
+ if (ix < 0x40360000) { /* |x|<22 */
+ if (ix < 0x3c800000) { /* |x|<2**-55 */
+ return x * (1.00000000000000000000e+00 + x);/* ieee_tanh(small) = small */
+ }
+
+ if (ix >= 0x3ff00000) { /* |x|>=1 */
+ t = Math.expm1(2.0 * Math.abs(x));
+ z = 1.00000000000000000000e+00 - 2.0 / (t + 2.0);
+ } else {
+ t = Math.expm1(-2.0 * Math.abs(x));
+ z = -t / (t + 2.0);
+ }
+ /* |x| > 22, return +-1 */
+ } else {
+ z = 1.00000000000000000000e+00 - TINY; /* raised inexact flag */
+ }
+ return (jx >= 0) ? z : -z;
+ }
/**
* Returns the measure in degrees of the supplied radian angle. The result
@@ -922,6 +1852,7 @@ public final class StrictMath {
/**
* Returns a double with the given magnitude and the sign of {@code sign}.
* If {@code sign} is NaN, the sign of the result is positive.
+ *
* @since 1.6
*/
public static double copySign(double magnitude, double sign) {
@@ -932,13 +1863,15 @@ public final class StrictMath {
// (Tested on a Nexus One.)
long magnitudeBits = Double.doubleToRawLongBits(magnitude);
long signBits = Double.doubleToRawLongBits((sign != sign) ? 1.0 : sign);
- magnitudeBits = (magnitudeBits & ~Double.SIGN_MASK) | (signBits & Double.SIGN_MASK);
+ magnitudeBits = (magnitudeBits & ~Double.SIGN_MASK)
+ | (signBits & Double.SIGN_MASK);
return Double.longBitsToDouble(magnitudeBits);
}
/**
- * Returns a float with the given magnitude and the sign of {@code sign}.
- * If {@code sign} is NaN, the sign of the result is positive.
+ * Returns a float with the given magnitude and the sign of {@code sign}. If
+ * {@code sign} is NaN, the sign of the result is positive.
+ *
* @since 1.6
*/
public static float copySign(float magnitude, float sign) {
@@ -949,12 +1882,14 @@ public final class StrictMath {
// (Tested on a Nexus One.)
int magnitudeBits = Float.floatToRawIntBits(magnitude);
int signBits = Float.floatToRawIntBits((sign != sign) ? 1.0f : sign);
- magnitudeBits = (magnitudeBits & ~Float.SIGN_MASK) | (signBits & Float.SIGN_MASK);
+ magnitudeBits = (magnitudeBits & ~Float.SIGN_MASK)
+ | (signBits & Float.SIGN_MASK);
return Float.intBitsToFloat(magnitudeBits);
}
/**
* Returns the exponent of float {@code f}.
+ *
* @since 1.6
*/
public static int getExponent(float f) {
@@ -963,14 +1898,17 @@ public final class StrictMath {
/**
* Returns the exponent of double {@code d}.
+ *
* @since 1.6
*/
- public static int getExponent(double d){
+ public static int getExponent(double d) {
return Math.getExponent(d);
}
/**
- * Returns the next double after {@code start} in the given {@code direction}.
+ * Returns the next double after {@code start} in the given
+ * {@code direction}.
+ *
* @since 1.6
*/
public static double nextAfter(double start, double direction) {
@@ -981,7 +1919,9 @@ public final class StrictMath {
}
/**
- * Returns the next float after {@code start} in the given {@code direction}.
+ * Returns the next float after {@code start} in the given {@code direction}
+ * .
+ *
* @since 1.6
*/
public static float nextAfter(float start, double direction) {
@@ -990,6 +1930,7 @@ public final class StrictMath {
/**
* Returns the next double larger than {@code d}.
+ *
* @since 1.6
*/
public static double nextUp(double d) {
@@ -998,6 +1939,7 @@ public final class StrictMath {
/**
* Returns the next float larger than {@code f}.
+ *
* @since 1.6
*/
public static float nextUp(float f) {
@@ -1006,6 +1948,7 @@ public final class StrictMath {
/**
* Returns {@code d} * 2^{@code scaleFactor}. The result may be rounded.
+ *
* @since 1.6
*/
public static double scalb(double d, int scaleFactor) {
@@ -1049,12 +1992,10 @@ public final class StrictMath {
} else {
if (Math.abs(d) >= Double.MIN_NORMAL) {
// common situation
- result = ((factor + Double.EXPONENT_BIAS) << Double.MANTISSA_BITS)
- | (bits & Double.MANTISSA_MASK);
+ result = ((factor + Double.EXPONENT_BIAS) << Double.MANTISSA_BITS) | (bits & Double.MANTISSA_MASK);
} else {
// origin d is sub-normal, change mantissa to normal style
- result = ((factor + Double.EXPONENT_BIAS) << Double.MANTISSA_BITS)
- | ((bits << (subNormalFactor + 1)) & Double.MANTISSA_MASK);
+ result = ((factor + Double.EXPONENT_BIAS) << Double.MANTISSA_BITS) | ((bits << (subNormalFactor + 1)) & Double.MANTISSA_MASK);
}
}
return Double.longBitsToDouble(result | sign);
@@ -1062,6 +2003,7 @@ public final class StrictMath {
/**
* Returns {@code d} * 2^{@code scaleFactor}. The result may be rounded.
+ *
* @since 1.6
*/
public static float scalb(float d, int scaleFactor) {
@@ -1073,8 +2015,7 @@ public final class StrictMath {
int factor = ((bits & Float.EXPONENT_MASK) >> Float.MANTISSA_BITS)
- Float.EXPONENT_BIAS + scaleFactor;
// calculates the factor of sub-normal values
- int subNormalFactor = Integer.numberOfLeadingZeros(bits & ~Float.SIGN_MASK)
- - Float.EXPONENT_BITS;
+ int subNormalFactor = Integer.numberOfLeadingZeros(bits & ~Float.SIGN_MASK) - Float.EXPONENT_BITS;
if (subNormalFactor < 0) {
// not sub-normal values
subNormalFactor = 0;
@@ -1105,8 +2046,9 @@ public final class StrictMath {
| (bits & Float.MANTISSA_MASK);
} else {
// origin d is sub-normal, change mantissa to normal style
- result = ((factor + Float.EXPONENT_BIAS) << Float.MANTISSA_BITS)
- | ((bits << (subNormalFactor + 1)) & Float.MANTISSA_MASK);
+ result = ((factor + Float.EXPONENT_BIAS)
+ << Float.MANTISSA_BITS) | (
+ (bits << (subNormalFactor + 1)) & Float.MANTISSA_MASK);
}
}
return Float.intBitsToFloat(result | sign);
@@ -1120,10 +2062,10 @@ public final class StrictMath {
}
// change it to positive
int absDigits = -digits;
- if (Integer.numberOfLeadingZeros(bits & ~Float.SIGN_MASK) <= (32 - absDigits)) {
+ if (Integer.numberOfLeadingZeros(bits & ~Float.SIGN_MASK)
+ <= (32 - absDigits)) {
// some bits will remain after shifting, calculates its carry
- if ((((bits >> (absDigits - 1)) & 0x1) == 0)
- || Integer.numberOfTrailingZeros(bits) == (absDigits - 1)) {
+ if ((((bits >> (absDigits - 1)) & 0x1) == 0) || Integer.numberOfTrailingZeros(bits) == (absDigits - 1)) {
return bits >> absDigits;
}
return ((bits >> absDigits) + 1);
@@ -1139,10 +2081,10 @@ public final class StrictMath {
}
// change it to positive
long absDigits = -digits;
- if (Long.numberOfLeadingZeros(bits & ~Double.SIGN_MASK) <= (64 - absDigits)) {
+ if (Long.numberOfLeadingZeros(bits & ~Double.SIGN_MASK)
+ <= (64 - absDigits)) {
// some bits will remain after shifting, calculates its carry
- if ((((bits >> (absDigits - 1)) & 0x1) == 0)
- || Long.numberOfTrailingZeros(bits) == (absDigits - 1)) {
+ if ((((bits >> (absDigits - 1)) & 0x1) == 0) || Long.numberOfTrailingZeros(bits) == (absDigits - 1)) {
return bits >> absDigits;
}
return ((bits >> absDigits) + 1);
diff --git a/luni/src/main/java/java/lang/System.java b/luni/src/main/java/java/lang/System.java
index 9425894..e79f844 100644
--- a/luni/src/main/java/java/lang/System.java
+++ b/luni/src/main/java/java/lang/System.java
@@ -50,6 +50,7 @@ import java.nio.channels.spi.SelectorProvider;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@@ -108,6 +109,33 @@ public final class System {
unchangeableSystemProperties = initUnchangeableSystemProperties();
systemProperties = createSystemProperties();
lineSeparator = System.getProperty("line.separator");
+
+ addLegacyLocaleSystemProperties();
+ }
+
+ private static void addLegacyLocaleSystemProperties() {
+ final String locale = getProperty("user.locale", "");
+ if (!locale.isEmpty()) {
+ Locale l = Locale.forLanguageTag(locale);
+ setUnchangeableSystemProperty("user.language", l.getLanguage());
+ setUnchangeableSystemProperty("user.region", l.getCountry());
+ setUnchangeableSystemProperty("user.variant", l.getVariant());
+ } else {
+ // If "user.locale" isn't set we fall back to our old defaults of
+ // language="en" and region="US" (if unset) and don't attempt to set it.
+ // The Locale class will fall back to using user.language and
+ // user.region if unset.
+ final String language = getProperty("user.language", "");
+ final String region = getProperty("user.region", "");
+
+ if (language.isEmpty()) {
+ setUnchangeableSystemProperty("user.language", "en");
+ }
+
+ if (region.isEmpty()) {
+ setUnchangeableSystemProperty("user.region", "US");
+ }
+ }
}
/**
@@ -751,8 +779,6 @@ public final class System {
p.put("java.vm.vendor.url", projectUrl);
p.put("file.encoding", "UTF-8");
- p.put("user.language", "en");
- p.put("user.region", "US");
try {
StructPasswd passwd = Libcore.os.getpwuid(Libcore.os.getuid());
@@ -771,6 +797,13 @@ public final class System {
p.put("android.icu.unicode.version", ICU.getUnicodeVersion());
p.put("android.icu.cldr.version", ICU.getCldrVersion());
+ // Property override for ICU4J : this is the location of the ICU4C data. This
+ // is prioritized over the properties in ICUConfig.properties. The issue with using
+ // that is that it doesn't play well with jarjar and it needs complicated build rules
+ // to change its default value.
+ String icuDataPath = generateIcuDataPath();
+ p.put("android.icu.impl.ICUBinary.dataPath", icuDataPath);
+
parsePropertyAssignments(p, specialProperties());
// Override built-in properties with settings from the command line.
@@ -780,9 +813,13 @@ public final class System {
/**
* Inits an unchangeable system property with the given value.
- * This is useful when the environment needs to change under native bridge emulation.
+ *
+ * This is called from native code when the environment needs to change under native
+ * bridge emulation.
+ *
+ * @hide also visible for tests.
*/
- private static void initUnchangeableSystemProperty(String name, String value) {
+ public static void setUnchangeableSystemProperty(String name, String value) {
checkPropertyName(name);
unchangeableSystemProperties.put(name, value);
}
@@ -807,6 +844,37 @@ public final class System {
return p;
}
+ private static String generateIcuDataPath() {
+ StringBuilder icuDataPathBuilder = new StringBuilder();
+ // ICU should first look in ANDROID_DATA. This is used for (optional) timezone data.
+ String dataIcuDataPath = getEnvironmentPath("ANDROID_DATA", "/misc/zoneinfo/current/icu");
+ if (dataIcuDataPath != null) {
+ icuDataPathBuilder.append(dataIcuDataPath);
+ }
+
+ // ICU should always look in ANDROID_ROOT.
+ String systemIcuDataPath = getEnvironmentPath("ANDROID_ROOT", "/usr/icu");
+ if (systemIcuDataPath != null) {
+ if (icuDataPathBuilder.length() > 0) {
+ icuDataPathBuilder.append(":");
+ }
+ icuDataPathBuilder.append(systemIcuDataPath);
+ }
+ return icuDataPathBuilder.toString();
+ }
+
+ /**
+ * Creates a path by combining the value of an environment variable with a relative path.
+ * Returns {@code null} if the environment variable is not set.
+ */
+ private static String getEnvironmentPath(String environmentVariable, String path) {
+ String variable = getenv(environmentVariable);
+ if (variable == null) {
+ return null;
+ }
+ return variable + path;
+ }
+
/**
* Returns an array of "key=value" strings containing information not otherwise
* easily available, such as #defined library versions.
@@ -964,10 +1032,12 @@ public final class System {
public static native int identityHashCode(Object anObject);
/**
- * Returns the system's line separator. On Android, this is {@code "\n"}. The value
- * comes from the value of the {@code line.separator} system property when the VM
- * starts. Later changes to the property will not affect the value returned by this
- * method.
+ * Returns the system's line separator. On Android, this is {@code "\n"}. The value comes from
+ * the value of the {@code line.separator} system property.
+ *
+ * <p>On Android versions before Lollipop the {@code line.separator} system property can be
+ * modified but this method continues to return the original value. The system property cannot
+ * be modified on later versions of Android.
* @since 1.7
*/
public static String lineSeparator() {
@@ -1106,7 +1176,12 @@ public final class System {
* named by the argument. On Android, this would turn {@code "MyLibrary"} into
* {@code "libMyLibrary.so"}.
*/
- public static native String mapLibraryName(String nickname);
+ public static String mapLibraryName(String nickname) {
+ if (nickname == null) {
+ throw new NullPointerException("nickname == null");
+ }
+ return "lib" + nickname + ".so";
+ }
/**
* Used to set System.err, System.in, and System.out.
diff --git a/luni/src/main/java/java/lang/reflect/Modifier.java b/luni/src/main/java/java/lang/reflect/Modifier.java
index 257064e..0480b8b 100644
--- a/luni/src/main/java/java/lang/reflect/Modifier.java
+++ b/luni/src/main/java/java/lang/reflect/Modifier.java
@@ -302,4 +302,23 @@ public class Modifier {
buf.setLength(buf.length() - 1);
return buf.toString();
}
+
+ /**
+ * Returns the modifiers for fields that can be present in a declaration.
+ * @hide
+ */
+ static String getDeclarationFieldModifiers(int modifiers) {
+ return Modifier.toString(modifiers & fieldModifiers());
+ }
+
+ /**
+ * Returns the modifiers for methods that can be present in a declaration.
+ * @hide
+ */
+ static String getDeclarationMethodModifiers(int modifiers) {
+ return Modifier.toString(modifiers & (
+ Modifier.isConstructor(modifiers)
+ ? Modifier.constructorModifiers()
+ : Modifier.methodModifiers()));
+ }
}
diff --git a/luni/src/main/java/java/net/DatagramSocket.java b/luni/src/main/java/java/net/DatagramSocket.java
index f9b72d8..3195240 100644
--- a/luni/src/main/java/java/net/DatagramSocket.java
+++ b/luni/src/main/java/java/net/DatagramSocket.java
@@ -56,7 +56,7 @@ public class DatagramSocket implements Closeable {
/**
* Constructs a UDP datagram socket which is bound to any available port on
- * the localhost.
+ * the local host using a wildcard address.
*
* @throws SocketException
* if an error occurs while creating or binding the socket.
@@ -67,34 +67,34 @@ public class DatagramSocket implements Closeable {
/**
* Constructs a UDP datagram socket which is bound to the specific port
- * {@code aPort} on the localhost. Valid values for {@code aPort} are
+ * {@code aPort} on the local host using a wildcard address. Valid values for {@code aPort} are
* between 0 and 65535 inclusive.
*
* @param aPort
- * the port to bind on the localhost.
+ * the port to bind on the local host.
* @throws SocketException
* if an error occurs while creating or binding the socket.
*/
public DatagramSocket(int aPort) throws SocketException {
checkPort(aPort);
- createSocket(aPort, Inet4Address.ANY);
+ createSocket(aPort, Inet6Address.ANY);
}
/**
- * Constructs a UDP datagram socket which is bound to the specific local
- * address {@code addr} on port {@code aPort}. Valid values for {@code
- * aPort} are between 0 and 65535 inclusive.
+ * Constructs a UDP datagram socket which is bound to the specific local address {@code addr} on
+ * port {@code aPort}. Valid values for {@code aPort} are between 0 and 65535 inclusive. If
+ * {@code addr} is {@code null} the socket will be bound to a wildcard address.
*
* @param aPort
- * the port to bind on the localhost.
+ * the port to bind on the local host.
* @param addr
- * the address to bind on the localhost.
+ * the address to bind on the local host.
* @throws SocketException
* if an error occurs while creating or binding the socket.
*/
public DatagramSocket(int aPort, InetAddress addr) throws SocketException {
checkPort(aPort);
- createSocket(aPort, (addr == null) ? Inet4Address.ANY : addr);
+ createSocket(aPort, (addr == null) ? Inet6Address.ANY : addr);
}
private void checkPort(int aPort) {
@@ -443,7 +443,7 @@ public class DatagramSocket implements Closeable {
private void ensureBound() throws SocketException {
if (!isBound()) {
- impl.bind(0, Inet4Address.ANY);
+ impl.bind(0, Inet6Address.ANY);
isBound = true;
}
}
@@ -467,7 +467,7 @@ public class DatagramSocket implements Closeable {
InetAddress addr;
if (localAddr == null) {
localPort = 0;
- addr = Inet4Address.ANY;
+ addr = Inet6Address.ANY;
} else {
if (!(localAddr instanceof InetSocketAddress)) {
throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
diff --git a/luni/src/main/java/java/net/DatagramSocketImpl.java b/luni/src/main/java/java/net/DatagramSocketImpl.java
index 1a39987..c5e7d70 100644
--- a/luni/src/main/java/java/net/DatagramSocketImpl.java
+++ b/luni/src/main/java/java/net/DatagramSocketImpl.java
@@ -43,13 +43,13 @@ public abstract class DatagramSocketImpl implements SocketOptions {
}
/**
- * Binds the datagram socket to the given localhost/port. Sockets must be
+ * Binds the datagram socket to the given local host/port. Sockets must be
* bound prior to attempting to send or receive data.
*
* @param port
- * the port on the localhost to bind.
+ * the port on the local host to bind to.
* @param addr
- * the address on the multihomed localhost to bind.
+ * the address on the multihomed local host to bind to.
* @throws SocketException
* if an error occurs while binding, for example, if the port
* has been already bound.
diff --git a/luni/src/main/java/java/net/Inet6Address.java b/luni/src/main/java/java/net/Inet6Address.java
index 8ab0f8d..347f43a 100644
--- a/luni/src/main/java/java/net/Inet6Address.java
+++ b/luni/src/main/java/java/net/Inet6Address.java
@@ -43,7 +43,7 @@ public final class Inet6Address extends InetAddress {
*/
public static final InetAddress LOOPBACK =
new Inet6Address(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
- "localhost", 0);
+ "ip6-localhost", 0);
private boolean scope_id_set;
private int scope_id;
diff --git a/luni/src/main/java/java/net/InetAddress.java b/luni/src/main/java/java/net/InetAddress.java
index 5cfa15a..581e1bd 100644
--- a/luni/src/main/java/java/net/InetAddress.java
+++ b/luni/src/main/java/java/net/InetAddress.java
@@ -202,10 +202,10 @@ public class InetAddress implements Serializable {
/**
* Gets all IP addresses associated with the given {@code host} identified
* by name or literal IP address. The IP address is resolved by the
- * configured name service. If the host name is empty or {@code null} an
- * {@code UnknownHostException} is thrown. If the host name is a literal IP
- * address string an array with the corresponding single {@code InetAddress}
- * is returned.
+ * configured name service. If the host name is empty or {@code null} the
+ * IP addresses of the loopback interfaces are returned. If the host name
+ * is a literal IP address string an array with the corresponding single
+ * {@code InetAddress} is returned.
*
* @param host the hostname or literal IP string to be resolved.
* @return the array of addresses associated with the specified host.
@@ -344,6 +344,19 @@ public class InetAddress implements Serializable {
}
/**
+ * Returns the hostname if known, or the result of {@code #getHostAddress}.
+ * Unlike {@link #getHostName}, this method will never cause a DNS lookup.
+ *
+ * @hide For libcore situations that must avoid DNS lookups.
+ */
+ public String getHostString() {
+ if (hostName == null) {
+ return getHostAddress();
+ }
+ return hostName;
+ }
+
+ /**
* Returns the fully qualified hostname corresponding to this IP address.
*/
public String getCanonicalHostName() {
diff --git a/luni/src/main/java/java/net/InetSocketAddress.java b/luni/src/main/java/java/net/InetSocketAddress.java
index a366133..1b3eabe 100644
--- a/luni/src/main/java/java/net/InetSocketAddress.java
+++ b/luni/src/main/java/java/net/InetSocketAddress.java
@@ -21,7 +21,7 @@ import java.io.IOException;
import java.io.ObjectInputStream;
/**
- * This class represents a socket endpoint described by a IP address and a port
+ * This class represents the address of a socket endpoint described by a IP address and a port
* number. It is a concrete implementation of {@code SocketAddress} for IP.
*/
public class InetSocketAddress extends SocketAddress {
@@ -48,8 +48,7 @@ public class InetSocketAddress extends SocketAddress {
* no specified address. The range for valid port numbers is between 0 and
* 65535 inclusive.
*
- * @param port
- * the specified port number to which this socket is bound.
+ * @param port the port number of the socket endpoint.
*/
public InetSocketAddress(int port) {
this((InetAddress) null, port);
@@ -58,19 +57,17 @@ public class InetSocketAddress extends SocketAddress {
/**
* Creates a socket endpoint with the given port number {@code port} and
* {@code address}. The range for valid port numbers is between 0 and 65535
- * inclusive. If {@code address} is {@code null} this socket is bound to the
- * IPv4 wildcard address.
+ * inclusive. If {@code address} is {@code null} the address is set to a
+ * wildcard address.
*
- * @param port
- * the specified port number to which this socket is bound.
- * @param address
- * the specified address to which this socket is bound.
+ * @param address the address of the socket endpoint.
+ * @param port the port number of the socket endpoint.
*/
public InetSocketAddress(InetAddress address, int port) {
if (port < 0 || port > 65535) {
throw new IllegalArgumentException("port=" + port);
}
- this.addr = (address == null) ? Inet4Address.ANY : address;
+ this.addr = (address == null) ? Inet6Address.ANY : address;
this.hostname = null;
this.port = port;
}
@@ -81,10 +78,8 @@ public class InetSocketAddress extends SocketAddress {
* {@code null}. The range for valid port numbers is between 0 and 65535
* inclusive.
*
- * @param port
- * the specified port number to which this socket is bound.
- * @param host
- * the specified hostname to which this socket is bound.
+ * @param host the hostname of the socket endpoint.
+ * @param port the port number of the socket endpoint.
*/
public InetSocketAddress(String host, int port) {
this(host, port, true);
@@ -117,28 +112,25 @@ public class InetSocketAddress extends SocketAddress {
* hostname into an {@code InetAddress}. The address field is marked as
* unresolved.
*
- * @param host
- * the specified hostname to which this socket is bound.
- * @param port
- * the specified port number to which this socket is bound.
+ * @param host the hostname of the socket endpoint.
+ * @param port the port number of the socket endpoint.
* @return the created InetSocketAddress instance.
- * @throws IllegalArgumentException
- * if the hostname {@code host} is {@code null} or the port is
- * not in the range between 0 and 65535.
+ * @throws IllegalArgumentException if the hostname {@code host} is {@code null} or the port is
+ * not in the range between 0 and 65535.
*/
public static InetSocketAddress createUnresolved(String host, int port) {
return new InetSocketAddress(host, port, false);
}
/**
- * Returns this socket address' port.
+ * Returns the socket endpoint's port.
*/
public final int getPort() {
return port;
}
/**
- * Returns this socket address' address.
+ * Returns the socket endpoint's address.
*/
public final InetAddress getAddress() {
return addr;
@@ -159,7 +151,7 @@ public class InetSocketAddress extends SocketAddress {
* @since 1.7
*/
public final String getHostString() {
- return (hostname != null) ? hostname : addr.getHostAddress();
+ return (hostname != null) ? hostname : addr.getHostString();
}
/**
@@ -186,9 +178,8 @@ public class InetSocketAddress extends SocketAddress {
* socket endpoints are equal if the IP address or the hostname of both are
* equal and they are bound to the same port.
*
- * @param socketAddr
- * the object to be tested for equality.
- * @return {@code true} if this socket and the given socket object {@code
+ * @param socketAddr the object to be tested for equality.
+ * @return {@code true} if this socket endpoint and the given socket endpoint {@code
* socketAddr} are equal, {@code false} otherwise.
*/
@Override
diff --git a/luni/src/main/java/java/net/PlainSocketImpl.java b/luni/src/main/java/java/net/PlainSocketImpl.java
index 4e5ba44..0c50f62 100644
--- a/luni/src/main/java/java/net/PlainSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainSocketImpl.java
@@ -301,16 +301,12 @@ public class PlainSocketImpl extends SocketImpl {
* Gets the InetAddress of the SOCKS proxy server.
*/
private InetAddress socksGetServerAddress() throws UnknownHostException {
- String proxyName;
// get socks server address from proxy. It is unnecessary to check
// "socksProxyHost" property, since all proxy setting should be
// determined by ProxySelector.
- InetSocketAddress addr = (InetSocketAddress) proxy.address();
- proxyName = addr.getHostName();
- if (proxyName == null) {
- proxyName = addr.getAddress().getHostAddress();
- }
- return InetAddress.getByName(proxyName);
+ InetSocketAddress socketAddress = (InetSocketAddress) proxy.address();
+ InetAddress address = socketAddress.getAddress();
+ return (address != null) ? address : InetAddress.getByName(socketAddress.getHostName());
}
/**
diff --git a/luni/src/main/java/java/net/ServerSocket.java b/luni/src/main/java/java/net/ServerSocket.java
index 72b197f..a2cd9c6 100644
--- a/luni/src/main/java/java/net/ServerSocket.java
+++ b/luni/src/main/java/java/net/ServerSocket.java
@@ -65,24 +65,25 @@ public class ServerSocket implements Closeable {
}
/**
- * Constructs a new {@code ServerSocket} instance bound to the given {@code port}.
- * The backlog is set to 50. If {@code port == 0}, a port will be assigned by the OS.
+ * Constructs a new {@code ServerSocket} instance bound to the given {@code port} using a
+ * wildcard address. The backlog is set to 50. If {@code port == 0}, a port will be assigned by
+ * the OS.
*
* @throws IOException if an error occurs while creating the socket.
*/
public ServerSocket(int port) throws IOException {
- this(port, DEFAULT_BACKLOG, Inet4Address.ANY);
+ this(port, DEFAULT_BACKLOG, Inet6Address.ANY);
}
/**
- * Constructs a new {@code ServerSocket} instance bound to the given {@code port}.
- * The backlog is set to {@code backlog}.
+ * Constructs a new {@code ServerSocket} instance bound to the given {@code port} using a
+ * wildcard address. The backlog is set to {@code backlog}.
* If {@code port == 0}, a port will be assigned by the OS.
*
* @throws IOException if an error occurs while creating the socket.
*/
public ServerSocket(int port, int backlog) throws IOException {
- this(port, backlog, Inet4Address.ANY);
+ this(port, backlog, Inet6Address.ANY);
}
/**
@@ -97,7 +98,7 @@ public class ServerSocket implements Closeable {
checkListen(port);
this.impl = factory != null ? factory.createSocketImpl()
: new PlainServerSocketImpl();
- InetAddress addr = (localAddress == null) ? Inet4Address.ANY : localAddress;
+ InetAddress addr = (localAddress == null) ? Inet6Address.ANY : localAddress;
synchronized (this) {
impl.create(true);
@@ -316,7 +317,7 @@ public class ServerSocket implements Closeable {
InetAddress addr;
int port;
if (localAddr == null) {
- addr = Inet4Address.ANY;
+ addr = Inet6Address.ANY;
port = 0;
} else {
if (!(localAddr instanceof InetSocketAddress)) {
diff --git a/luni/src/main/java/java/net/Socket.java b/luni/src/main/java/java/net/Socket.java
index 5dd350a..7ee6b05 100644
--- a/luni/src/main/java/java/net/Socket.java
+++ b/luni/src/main/java/java/net/Socket.java
@@ -41,7 +41,7 @@ public class Socket implements Closeable {
private boolean isInputShutdown = false;
private boolean isOutputShutdown = false;
- private InetAddress localAddress = Inet4Address.ANY;
+ private InetAddress localAddress = Inet6Address.ANY;
private final Object connectLock = new Object();
@@ -211,7 +211,7 @@ public class Socket implements Closeable {
/**
* Creates a new streaming socket connected to the target host specified by
* the parameters {@code dstAddress} and {@code dstPort}. The socket is
- * bound to any available port on the local host.
+ * bound to any available port on the local host using a wildcard address.
*
* @param dstAddress
* the target host address to connect to.
@@ -228,16 +228,18 @@ public class Socket implements Closeable {
/**
* Creates a new streaming socket connected to the target host specified by
- * the parameters {@code dstAddress} and {@code dstPort}. On the local
- * endpoint the socket is bound to the given address {@code localAddress} on
- * port {@code localPort}.
+ * the parameters {@code dstAddress} and {@code dstPort}.
+ *
+ * <p>On the local endpoint the socket is bound to the given address {@code localAddress} on
+ * port {@code localPort}. If {@code localAddress} is {@code null} the socket will be bound to a
+ * wildcard address.
*
* @param dstAddress
* the target host address to connect to.
* @param dstPort
* the port on the target host to connect to.
* @param localAddress
- * the address on the local host to bind to.
+ * the address on the local host to bind to, or null.
* @param localPort
* the port on the local host to bind to.
* @throws IOException
@@ -253,7 +255,7 @@ public class Socket implements Closeable {
/**
* Creates a new streaming or datagram socket connected to the target host
* specified by the parameters {@code addr} and {@code port}. The socket is
- * bound to any available port on the local host.
+ * bound to any available port on the local host using a wildcard address.
*
* @param addr
* the Internet address to connect to.
@@ -315,7 +317,7 @@ public class Socket implements Closeable {
isConnected = false;
// RI compatibility: the RI returns the any address (but the original local port) after
// close.
- localAddress = Inet4Address.ANY;
+ localAddress = Inet6Address.ANY;
impl.close();
}
@@ -330,7 +332,7 @@ public class Socket implements Closeable {
isConnected = false;
// RI compatibility: the RI returns the any address (but the original local port) after
// close.
- localAddress = Inet4Address.ANY;
+ localAddress = Inet6Address.ANY;
impl.onClose();
}
@@ -577,7 +579,7 @@ public class Socket implements Closeable {
throw new IllegalArgumentException("Local port out of range: " + localPort);
}
- InetAddress addr = localAddress == null ? Inet4Address.ANY : localAddress;
+ InetAddress addr = localAddress == null ? Inet6Address.ANY : localAddress;
synchronized (this) {
impl.create(streaming);
isCreated = true;
@@ -772,7 +774,7 @@ public class Socket implements Closeable {
InetAddress addr;
if (localAddr == null) {
port = 0;
- addr = Inet4Address.ANY;
+ addr = Inet6Address.ANY;
} else {
if (!(localAddr instanceof InetSocketAddress)) {
throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
@@ -875,7 +877,7 @@ public class Socket implements Closeable {
// options on create
// impl.create(true);
if (!usingSocks()) {
- impl.bind(Inet4Address.ANY, 0);
+ impl.bind(Inet6Address.ANY, 0);
}
isBound = true;
}
diff --git a/luni/src/main/java/java/net/URL.java b/luni/src/main/java/java/net/URL.java
index dd487bc..b62ed84 100644
--- a/luni/src/main/java/java/net/URL.java
+++ b/luni/src/main/java/java/net/URL.java
@@ -331,7 +331,7 @@ public final class URL implements Serializable {
* Virtual hosting permits unrelated sites to share an IP address. This
* method could report two otherwise unrelated URLs to be equal because
* they're hosted on the same server.</li>
- * <li><strong>The network many not be available.</strong> Two URLs could be
+ * <li><strong>The network may not be available.</strong> Two URLs could be
* equal when a network is available and unequal otherwise.</li>
* <li><strong>The network may change.</strong> The IP address for a given
* host name varies by network and over time. This is problematic for mobile
diff --git a/luni/src/main/java/java/nio/SelectorImpl.java b/luni/src/main/java/java/nio/SelectorImpl.java
index 45406b1..c73ba22 100644
--- a/luni/src/main/java/java/nio/SelectorImpl.java
+++ b/luni/src/main/java/java/nio/SelectorImpl.java
@@ -38,7 +38,6 @@ import libcore.io.IoBridge;
import libcore.io.IoUtils;
import libcore.io.Libcore;
-import static android.system.OsConstants.EINTR;
import static android.system.OsConstants.POLLERR;
import static android.system.OsConstants.POLLHUP;
import static android.system.OsConstants.POLLIN;
@@ -94,7 +93,7 @@ final class SelectorImpl extends AbstractSelector {
* configure the pipe so we can fully drain it without blocking.
*/
try {
- FileDescriptor[] pipeFds = Libcore.os.pipe();
+ FileDescriptor[] pipeFds = Libcore.os.pipe2(0);
wakeupIn = pipeFds[0];
wakeupOut = pipeFds[1];
IoUtils.setBlocking(wakeupIn, false);
@@ -183,9 +182,7 @@ final class SelectorImpl extends AbstractSelector {
try {
rc = Libcore.os.poll(pollFds.array(), (int) timeout);
} catch (ErrnoException errnoException) {
- if (errnoException.errno != EINTR) {
- throw errnoException.rethrowAsIOException();
- }
+ throw errnoException.rethrowAsIOException();
}
} finally {
if (isBlocking) {
diff --git a/luni/src/main/java/java/nio/channels/Selector.java b/luni/src/main/java/java/nio/channels/Selector.java
index 6d9b063..baa6a7f 100644
--- a/luni/src/main/java/java/nio/channels/Selector.java
+++ b/luni/src/main/java/java/nio/channels/Selector.java
@@ -78,8 +78,11 @@ public abstract class Selector implements Closeable {
public abstract boolean isOpen();
/**
- * Gets the set of registered keys. The set is immutable and is not thread-
- * safe.
+ * Gets the set of registered keys.
+ *
+ * <p>The returned set cannot be changed directly but can be modified
+ * indirectly by operations on the Selector. It should therefore not be
+ * treated as thread-safe.
*
* @return the set of registered keys.
*/
@@ -127,9 +130,11 @@ public abstract class Selector implements Closeable {
public abstract int select(long timeout) throws IOException;
/**
- * Gets the selection keys whose channels are ready for operation. The set
- * is not thread-safe and no keys may be added to it. Removing keys is
- * allowed.
+ * Gets the selection keys whose channels are ready for operation.
+ *
+ * <p>Keys cannot be added to the set directly. Keys can be removed.
+ * The set can be modified indirectly by operations on the Selector. It
+ * should therefore not be treated as thread-safe.
*
* @return the selection keys whose channels are ready for operation.
* @throws ClosedSelectorException
diff --git a/luni/src/main/java/java/nio/charset/CharsetEncoder.java b/luni/src/main/java/java/nio/charset/CharsetEncoder.java
index 9217bba..9d53328 100644
--- a/luni/src/main/java/java/nio/charset/CharsetEncoder.java
+++ b/luni/src/main/java/java/nio/charset/CharsetEncoder.java
@@ -194,13 +194,19 @@ public abstract class CharsetEncoder {
throw illegalStateException();
}
+ if (!cb.hasRemaining()) {
+ return true;
+ }
+
CodingErrorAction originalMalformedInputAction = malformedInputAction;
CodingErrorAction originalUnmappableCharacterAction = unmappableCharacterAction;
onMalformedInput(CodingErrorAction.REPORT);
onUnmappableCharacter(CodingErrorAction.REPORT);
try {
- encode(cb);
- return true;
+ ByteBuffer buf = encode(cb);
+ // b/18474439: ICU will return U_ZERO_ERROR but produce an output buffer
+ // of size zero when it encounters an ignorable codepoint.
+ return buf.hasRemaining();
} catch (CharacterCodingException e) {
return false;
} finally {
@@ -436,24 +442,33 @@ public abstract class CharsetEncoder {
* <p>
* This method will call {@link #implFlush(ByteBuffer) implFlush}. Some
* encoders may need to write some bytes to the output buffer when they have
- * read all input characters, subclasses can overridden
- * {@link #implFlush(ByteBuffer) implFlush} to perform writing action.
+ * read all input characters. Subclasses can override
+ * {@link #implFlush(ByteBuffer) implFlush} to perform any writes that are
+ * required at the end of the output sequence, such as footers and other
+ * metadata.
* <p>
- * The maximum number of written bytes won't larger than
- * {@link ByteBuffer#remaining() out.remaining()}. If some encoder wants to
+ * The maximum number of written bytes won't be larger than
+ * {@link ByteBuffer#remaining() out.remaining()}. If the encoder wants to
* write more bytes than the output buffer's available remaining space, then
- * <code>CoderResult.OVERFLOW</code> will be returned, and this method
- * must be called again with a byte buffer that has free space. Otherwise
- * this method will return <code>CoderResult.UNDERFLOW</code>, which
- * means one encoding process has been completed successfully.
+ * it will return {@code CoderResult.OVERFLOW}. This method must then be
+ * called again with a byte buffer that has free space.
+ * <p>
+ * If the encoder was asked to flush its output when its input is incomplete,
+ * (because it ends with an unpaired surrogate, say) it may return
+ * {@code CodeResult.MALFORMED}.
+ * <p>
+ * In all other cases the encoder will return {@code CoderResult.UNDERFLOW},
+ * which signifies that all the input so far has been successfully encoded.
* <p>
* During the flush, the output buffer's position will be changed
* accordingly, while its mark and limit will be intact.
+ * <p>
+ * This method is a no-op if the encoder has already been flushed.
*
- * @param out
- * the given output buffer.
- * @return <code>CoderResult.UNDERFLOW</code> or
- * <code>CoderResult.OVERFLOW</code>.
+ * @param out the given output buffer.
+ * @return {@code CoderResult.UNDERFLOW} or
+ * {@code CoderResult.OVERFLOW} or
+ * {@code CoderResult.MALFORMED}
* @throws IllegalStateException
* if this encoder isn't already flushed or at end of input.
*/
@@ -461,6 +476,9 @@ public abstract class CharsetEncoder {
if (state != FLUSHED && state != END_OF_INPUT) {
throw illegalStateException();
}
+ if (state == FLUSHED) {
+ return CoderResult.UNDERFLOW;
+ }
CoderResult result = implFlush(out);
if (result == CoderResult.UNDERFLOW) {
state = FLUSHED;
diff --git a/luni/src/main/java/java/security/Security.java b/luni/src/main/java/java/security/Security.java
index b859f9a..aeb189f 100644
--- a/luni/src/main/java/java/security/Security.java
+++ b/luni/src/main/java/java/security/Security.java
@@ -19,6 +19,8 @@ package java.security;
import java.io.BufferedInputStream;
import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
@@ -47,16 +49,25 @@ public final class Security {
// - load security properties files
// - load statically registered providers
// - if no provider description file found then load default providers
+ // Note: Getting the input stream for the security.properties file is factored into its own
+ // function, which will be intercepted during boot image creation.
static {
boolean loaded = false;
+ Reader input = null;
try {
- InputStream configStream = Security.class.getResourceAsStream("security.properties");
- InputStream input = new BufferedInputStream(configStream);
+ input = getSecurityPropertiesReader();
secprops.load(input);
loaded = true;
- configStream.close();
} catch (Exception ex) {
System.logE("Could not load 'security.properties'", ex);
+ } finally {
+ if (input != null) {
+ try {
+ input.close();
+ } catch (Exception ex) {
+ System.logW("Could not close 'security.properties'", ex);
+ }
+ }
}
if (!loaded) {
registerDefaultProviders();
@@ -64,6 +75,11 @@ public final class Security {
Engine.door = new SecurityDoor();
}
+ private static Reader getSecurityPropertiesReader() throws Exception {
+ InputStream configStream = Security.class.getResourceAsStream("security.properties");
+ return new InputStreamReader(new BufferedInputStream(configStream), "ISO-8859-1");
+ }
+
/**
* This class can't be instantiated.
*/
diff --git a/luni/src/main/java/java/security/Signature.java b/luni/src/main/java/java/security/Signature.java
index a39d59b..3151058 100644
--- a/luni/src/main/java/java/security/Signature.java
+++ b/luni/src/main/java/java/security/Signature.java
@@ -193,7 +193,7 @@ public abstract class Signature extends SignatureSpi {
if (service == null) {
return null;
}
- return tryAlgorithmWithProvider(key, service);
+ return tryAlgorithmWithProvider(null, service);
}
ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
if (services == null) {
@@ -245,6 +245,15 @@ public abstract class Signature extends SignatureSpi {
}
/**
+ * Gets the SPI implementation backing this signature.
+ *
+ * @hide
+ */
+ public SignatureSpi getSpi() {
+ return null;
+ }
+
+ /**
* Returns the name of the algorithm of this {@code Signature}.
*
* @return the name of the algorithm of this {@code Signature}.
@@ -730,8 +739,11 @@ public abstract class Signature extends SignatureSpi {
/**
* Convenience call when the Key is not available.
+ *
+ * @hide
*/
- private SignatureSpi getSpi() {
+ @Override
+ public SignatureSpi getSpi() {
return getSpi(null);
}
}
diff --git a/luni/src/main/java/java/text/BreakIterator.java b/luni/src/main/java/java/text/BreakIterator.java
index 81545b2..051ea15 100644
--- a/luni/src/main/java/java/text/BreakIterator.java
+++ b/luni/src/main/java/java/text/BreakIterator.java
@@ -18,8 +18,6 @@
package java.text;
import java.util.Locale;
-import libcore.icu.ICU;
-import libcore.icu.NativeBreakIterator;
/**
* Locates boundaries in text. This class defines a protocol for objects that
@@ -230,29 +228,19 @@ public abstract class BreakIterator implements Cloneable {
*/
public static final int DONE = -1;
- // the wrapped ICU implementation
- NativeBreakIterator wrapped;
-
/**
* Default constructor, for use by subclasses.
*/
protected BreakIterator() {
}
- /*
- * wrapping constructor
- */
- BreakIterator(NativeBreakIterator iterator) {
- wrapped = iterator;
- }
-
/**
* Returns an array of locales for which custom {@code BreakIterator} instances
* are available.
* <p>Note that Android does not support user-supplied locale service providers.
*/
public static Locale[] getAvailableLocales() {
- return ICU.getAvailableBreakIteratorLocales();
+ return com.ibm.icu.text.BreakIterator.getAvailableLocales();
}
/**
@@ -270,7 +258,8 @@ public abstract class BreakIterator implements Cloneable {
* characters using the given locale.
*/
public static BreakIterator getCharacterInstance(Locale locale) {
- return new RuleBasedBreakIterator(NativeBreakIterator.getCharacterInstance(locale));
+ return new IcuIteratorWrapper(
+ com.ibm.icu.text.BreakIterator.getCharacterInstance(locale));
}
/**
@@ -288,7 +277,8 @@ public abstract class BreakIterator implements Cloneable {
* line breaks using the given locale.
*/
public static BreakIterator getLineInstance(Locale locale) {
- return new RuleBasedBreakIterator(NativeBreakIterator.getLineInstance(locale));
+ return new IcuIteratorWrapper(
+ com.ibm.icu.text.BreakIterator.getLineInstance(locale));
}
/**
@@ -306,7 +296,8 @@ public abstract class BreakIterator implements Cloneable {
* sentence-breaks using the given locale.
*/
public static BreakIterator getSentenceInstance(Locale locale) {
- return new RuleBasedBreakIterator(NativeBreakIterator.getSentenceInstance(locale));
+ return new IcuIteratorWrapper(
+ com.ibm.icu.text.BreakIterator.getSentenceInstance(locale));
}
/**
@@ -324,7 +315,8 @@ public abstract class BreakIterator implements Cloneable {
* word-breaks using the given locale.
*/
public static BreakIterator getWordInstance(Locale locale) {
- return new RuleBasedBreakIterator(NativeBreakIterator.getWordInstance(locale));
+ return new IcuIteratorWrapper(
+ com.ibm.icu.text.BreakIterator.getWordInstance(locale));
}
/**
@@ -339,7 +331,7 @@ public abstract class BreakIterator implements Cloneable {
* false} otherwise.
*/
public boolean isBoundary(int offset) {
- return wrapped.isBoundary(offset);
+ return false;
}
/**
@@ -354,7 +346,7 @@ public abstract class BreakIterator implements Cloneable {
* if the offset is invalid.
*/
public int preceding(int offset) {
- return wrapped.preceding(offset);
+ return 0;
}
/**
@@ -366,10 +358,6 @@ public abstract class BreakIterator implements Cloneable {
* the new text string to be analyzed.
*/
public void setText(String newText) {
- if (newText == null) {
- throw new NullPointerException("newText == null");
- }
- wrapped.setText(newText);
}
/**
@@ -466,9 +454,7 @@ public abstract class BreakIterator implements Cloneable {
@Override
public Object clone() {
try {
- BreakIterator cloned = (BreakIterator) super.clone();
- cloned.wrapped = (NativeBreakIterator) wrapped.clone();
- return cloned;
+ return (BreakIterator) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
diff --git a/luni/src/main/java/java/text/ChoiceFormat.java b/luni/src/main/java/java/text/ChoiceFormat.java
index 014b8c7..3d3cb3a 100644
--- a/luni/src/main/java/java/text/ChoiceFormat.java
+++ b/luni/src/main/java/java/text/ChoiceFormat.java
@@ -303,37 +303,16 @@ public class ChoiceFormat extends NumberFormat {
}
/**
- * Returns the double value which is closest to the specified double but
- * larger.
- *
- * @param value
- * a double value.
- * @return the next larger double value.
+ * Equivalent to {@link Math#nextUp(double)}.
*/
public static final double nextDouble(double value) {
- if (value == Double.POSITIVE_INFINITY) {
- return value;
- }
- long bits;
- // Handle -0.0
- if (value == 0) {
- bits = 0;
- } else {
- bits = Double.doubleToLongBits(value);
- }
- return Double.longBitsToDouble(value < 0 ? bits - 1 : bits + 1);
+ return Math.nextUp(value);
}
/**
- * Returns the double value which is closest to the specified double but
- * either larger or smaller as specified.
- *
- * @param value
- * a double value.
- * @param increment
- * {@code true} to get the next larger value, {@code false} to
- * get the previous smaller value.
- * @return the next larger or smaller double value.
+ * Equivalent to {@link Math#nextUp(double)} if {@code increment == true}, and
+ * {@link Math#nextAfter(double, double)} with {@code direction == Double.NEGATIVE_INFINITY}
+ * otherwise.
*/
public static double nextDouble(double value, boolean increment) {
return increment ? nextDouble(value) : previousDouble(value);
@@ -385,25 +364,11 @@ public class ChoiceFormat extends NumberFormat {
}
/**
- * Returns the double value which is closest to the specified double but
- * smaller.
- *
- * @param value
- * a double value.
- * @return the next smaller double value.
+ * Equivalent to {@link Math#nextAfter(double, double)} with
+ * {@code direction == Double.NEGATIVE_INFINITY}.
*/
public static final double previousDouble(double value) {
- if (value == Double.NEGATIVE_INFINITY) {
- return value;
- }
- long bits;
- // Handle 0.0
- if (value == 0) {
- bits = 0x8000000000000000L;
- } else {
- bits = Double.doubleToLongBits(value);
- }
- return Double.longBitsToDouble(value <= 0 ? bits + 1 : bits - 1);
+ return Math.nextAfter(value, Double.NEGATIVE_INFINITY);
}
/**
@@ -453,9 +418,30 @@ public class ChoiceFormat extends NumberFormat {
if (i != 0) {
buffer.append('|');
}
- String previous = String.valueOf(previousDouble(choiceLimits[i]));
- String limit = String.valueOf(choiceLimits[i]);
- if (previous.length() < limit.length()) {
+
+ final String previous = String.valueOf(previousDouble(choiceLimits[i]));
+ final String limit = String.valueOf(choiceLimits[i]);
+
+ // Hack to make the output of toPattern parseable by another ChoiceFormat.
+ // String.valueOf() will emit "Infinity", which isn't parseable by our NumberFormat
+ // instances.
+ //
+ // Ideally, we'd just use NumberFormat.format() to emit output (to be symmetric with
+ // our usage of NumberFormat.parse()) but it's hard set the right number of significant
+ // digits in order to output a format string that's equivalent to the original input.
+ if (Double.isInfinite(choiceLimits[i]) ||
+ Double.isInfinite(previousDouble(choiceLimits[i]))) {
+ if (choiceLimits[i] < 0) {
+ buffer.append("-\u221E");
+ buffer.append('<');
+ } else {
+ buffer.append('\u221E');
+ buffer.append('<');
+ }
+ } else if (previous.length() < limit.length()) {
+ // What the... i don't even.... sigh. This is trying to figure out whether the
+ // element was a "<" or a "#". The idea being that users will specify "reasonable"
+ // quantities and calling nextDouble will result in a "longer" number in most cases.
buffer.append(previous);
buffer.append('<');
} else {
diff --git a/luni/src/main/java/java/text/Format.java b/luni/src/main/java/java/text/Format.java
index 58671fa..c4dc5f0 100644
--- a/luni/src/main/java/java/text/Format.java
+++ b/luni/src/main/java/java/text/Format.java
@@ -177,23 +177,26 @@ public abstract class Format implements Serializable, Cloneable {
static boolean upTo(String string, ParsePosition position,
StringBuffer buffer, char stop) {
int index = position.getIndex(), length = string.length();
- boolean lastQuote = false, quote = false;
+
+ int numConsecutiveQuotes = 0;
+ boolean quote = false;
while (index < length) {
char ch = string.charAt(index++);
if (ch == '\'') {
- if (lastQuote) {
+ ++numConsecutiveQuotes;
+ if (numConsecutiveQuotes != 0 && numConsecutiveQuotes % 2 == 0) {
buffer.append('\'');
}
quote = !quote;
- lastQuote = true;
} else if (ch == stop && !quote) {
position.setIndex(index);
return true;
} else {
- lastQuote = false;
+ numConsecutiveQuotes = 0;
buffer.append(ch);
}
}
+
position.setIndex(index);
return false;
}
diff --git a/luni/src/main/java/java/text/RuleBasedBreakIterator.java b/luni/src/main/java/java/text/IcuIteratorWrapper.java
index a16968e..b863fa7 100644
--- a/luni/src/main/java/java/text/RuleBasedBreakIterator.java
+++ b/luni/src/main/java/java/text/IcuIteratorWrapper.java
@@ -17,7 +17,6 @@
package java.text;
-import libcore.icu.NativeBreakIterator;
/*
* Default implementation of BreakIterator. Wraps libcore.icu.NativeBreakIterator.
@@ -25,10 +24,13 @@ import libcore.icu.NativeBreakIterator;
* and we don't have Java implementations of those methods (other than the current ones, which
* forward to the wrapped NativeBreakIterator).
*/
-class RuleBasedBreakIterator extends BreakIterator {
+class IcuIteratorWrapper extends BreakIterator {
- RuleBasedBreakIterator(NativeBreakIterator iterator) {
- super(iterator);
+ /* The wrapped ICU implementation. Non-final for #clone() */
+ private com.ibm.icu.text.BreakIterator wrapped;
+
+ IcuIteratorWrapper(com.ibm.icu.text.BreakIterator iterator) {
+ wrapped = iterator;
}
@Override public int current() {
@@ -44,17 +46,6 @@ class RuleBasedBreakIterator extends BreakIterator {
return wrapped.following(offset);
}
- private void checkOffset(int offset) {
- if (!wrapped.hasText()) {
- throw new IllegalArgumentException("BreakIterator has no text");
- }
- CharacterIterator it = wrapped.getText();
- if (offset < it.getBeginIndex() || offset > it.getEndIndex()) {
- String message = "Valid range is [" + it.getBeginIndex() + " " + it.getEndIndex() + "]";
- throw new IllegalArgumentException(message);
- }
- }
-
@Override public CharacterIterator getText() {
return wrapped.getText();
}
@@ -75,10 +66,11 @@ class RuleBasedBreakIterator extends BreakIterator {
return wrapped.previous();
}
+ @Override public void setText(String newText) {
+ wrapped.setText(newText);
+ }
+
@Override public void setText(CharacterIterator newText) {
- if (newText == null) {
- throw new NullPointerException("newText == null");
- }
newText.current();
wrapped.setText(newText);
}
@@ -94,10 +86,10 @@ class RuleBasedBreakIterator extends BreakIterator {
}
@Override public boolean equals(Object o) {
- if (!(o instanceof RuleBasedBreakIterator)) {
+ if (!(o instanceof IcuIteratorWrapper)) {
return false;
}
- return wrapped.equals(((RuleBasedBreakIterator) o).wrapped);
+ return wrapped.equals(((IcuIteratorWrapper) o).wrapped);
}
@Override public String toString() {
@@ -108,9 +100,20 @@ class RuleBasedBreakIterator extends BreakIterator {
return wrapped.hashCode();
}
+ private void checkOffset(int offset) {
+ final CharacterIterator it = wrapped.getText();
+ if (it == null) {
+ throw new IllegalArgumentException("BreakIterator has no text");
+ }
+ if (offset < it.getBeginIndex() || offset > it.getEndIndex()) {
+ String message = "Valid range is [" + it.getBeginIndex() + " " + it.getEndIndex() + "]";
+ throw new IllegalArgumentException(message);
+ }
+ }
+
@Override public Object clone() {
- RuleBasedBreakIterator cloned = (RuleBasedBreakIterator) super.clone();
- cloned.wrapped = (NativeBreakIterator) wrapped.clone();
+ IcuIteratorWrapper cloned = (IcuIteratorWrapper) super.clone();
+ cloned.wrapped = (com.ibm.icu.text.BreakIterator) wrapped.clone();
return cloned;
}
}
diff --git a/luni/src/main/java/java/text/MessageFormat.java b/luni/src/main/java/java/text/MessageFormat.java
index cf306a7..f48cebd 100644
--- a/luni/src/main/java/java/text/MessageFormat.java
+++ b/luni/src/main/java/java/text/MessageFormat.java
@@ -580,6 +580,12 @@ public class MessageFormat extends Format {
}
if (format instanceof ChoiceFormat) {
String result = format.format(arg);
+ // Escape quotes in the result because the ChoiceFormat would've already
+ // dealt with them for us. In other words, any quotes that are present in the
+ // result are due to escaped quotes in the original input. We should preserve
+ // them in the output instead of having them processed again with the message
+ // format we're creating below.
+ result = result.replace("'", "''");
MessageFormat mf = new MessageFormat(result);
mf.setLocale(locale);
mf.format(objects, buffer, passedField);
diff --git a/luni/src/main/java/java/text/NumberFormat.java b/luni/src/main/java/java/text/NumberFormat.java
index a1f10d4..2526ffc 100644
--- a/luni/src/main/java/java/text/NumberFormat.java
+++ b/luni/src/main/java/java/text/NumberFormat.java
@@ -492,6 +492,10 @@ public abstract class NumberFormat extends Format {
* to format floating-point numbers typically between 0 and 1 (with 1 being 100%).
* A value such as 0.53 will be treated as 53%, but 53.0 (or the integer 53) will be
* treated as 5,300%, which is rarely what you intended.
+ *
+ * <p>Non-integer percentages will be rounded according to the rounding mode,
+ * so by default 0.142 will be 14% but 0.148 will be 15%. If you want fractional
+ * percentages, use {@link #setMaximumFractionDigits}.
*/
public static final NumberFormat getPercentInstance() {
return getPercentInstance(Locale.getDefault());
@@ -505,6 +509,10 @@ public abstract class NumberFormat extends Format {
* to format floating-point numbers typically between 0 and 1 (with 1 being 100%).
* A value such as 0.53 will be treated as 53%, but 53.0 (or the integer 53) will be
* treated as 5,300%, which is rarely what you intended.
+ *
+ * <p>Non-integer percentages will be rounded according to the rounding mode,
+ * so by default 0.142 will be 14% but 0.148 will be 15%. If you want fractional
+ * percentages, use {@link #setMaximumFractionDigits}.
*/
public static NumberFormat getPercentInstance(Locale locale) {
if (locale == null) {
diff --git a/luni/src/main/java/java/text/SimpleDateFormat.java b/luni/src/main/java/java/text/SimpleDateFormat.java
index 8f83ff7..4e7a950 100644
--- a/luni/src/main/java/java/text/SimpleDateFormat.java
+++ b/luni/src/main/java/java/text/SimpleDateFormat.java
@@ -91,7 +91,16 @@ import libcore.icu.TimeZoneNames;
* <tr> <td>{@code ''}</td> <td>single quote</td> <td>(Literal)</td> <td>{@code 'o''clock'}:o'clock</td> </tr>
* </table>
*
- * <p>Fractional seconds are handled specially: they're zero-padded on the <i>right</i>.
+ * <p>Note that {@code 'S'} represents fractional seconds and not millisecond values.
+ * They will be padded on the left or on the right or both depending on the number of
+ * {@code 'S'} in the pattern. For example, the number of fractional seconds in a
+ * {@code Date} where {@code Date.getTime() == 1000006} are {@code 0.006} or
+ * {@code (6 / 1000)}. This leads to the following formatting:
+ * <ul>
+ * <li> {@code "S" => "0"} </li>
+ * <li> {@code "SSS" => "006" } </li>
+ * <li> {@code "SSSSSS" => "006000" }</li>
+ * </ul>
*
* <p>The two pattern characters {@code L} and {@code c} are ICU-compatible extensions, not
* available in the RI or in Android before Android 2.3 (Gingerbread, API level 9). These
diff --git a/luni/src/main/java/java/util/Calendar.java b/luni/src/main/java/java/util/Calendar.java
index fc4cef6..6057547 100644
--- a/luni/src/main/java/java/util/Calendar.java
+++ b/luni/src/main/java/java/util/Calendar.java
@@ -810,7 +810,9 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
}
/**
- * Returns a shallow copy of this {@code Calendar} with the same properties.
+ * Returns a partially deep copy of this {@code Calendar}; all fields from
+ * from the {@code Calendar} class are cloned (deep copy) but fields from
+ * subclasses aren't (shallow copy).
*/
@Override
public Object clone() {
diff --git a/luni/src/main/java/java/util/Collections.java b/luni/src/main/java/java/util/Collections.java
index 4541d64..dc4161f 100644
--- a/luni/src/main/java/java/util/Collections.java
+++ b/luni/src/main/java/java/util/Collections.java
@@ -102,6 +102,10 @@ public class Collections {
throw new IndexOutOfBoundsException();
}
+ @Override public Iterator iterator() {
+ return EMPTY_ITERATOR;
+ }
+
private Object readResolve() {
return Collections.EMPTY_LIST;
}
@@ -1422,21 +1426,21 @@ public class Collections {
if (!(list instanceof RandomAccess)) {
ListIterator<? extends Comparable<? super T>> it = list.listIterator();
while (it.hasNext()) {
- int result;
- if ((result = -it.next().compareTo(object)) <= 0) {
- if (result == 0) {
- return it.previousIndex();
- }
+ final int result = it.next().compareTo(object);
+ if (result == 0) {
+ return it.previousIndex();
+ } else if (result > 0) {
return -it.previousIndex() - 1;
}
}
return -list.size() - 1;
}
- int low = 0, mid = list.size(), high = mid - 1, result = -1;
+ int low = 0, mid = list.size(), high = mid - 1, result = 1;
while (low <= high) {
mid = (low + high) >>> 1;
- if ((result = -list.get(mid).compareTo(object)) > 0) {
+ result = list.get(mid).compareTo(object);
+ if (result < 0) {
low = mid + 1;
} else if (result == 0) {
return mid;
@@ -1444,7 +1448,7 @@ public class Collections {
high = mid - 1;
}
}
- return -mid - (result < 0 ? 1 : 2);
+ return -mid - (result > 0 ? 1 : 2);
}
/**
@@ -1477,21 +1481,21 @@ public class Collections {
if (!(list instanceof RandomAccess)) {
ListIterator<? extends T> it = list.listIterator();
while (it.hasNext()) {
- int result;
- if ((result = -comparator.compare(it.next(), object)) <= 0) {
- if (result == 0) {
- return it.previousIndex();
- }
+ final int result = comparator.compare(it.next(), object);
+ if (result == 0) {
+ return it.previousIndex();
+ } else if (result > 0) {
return -it.previousIndex() - 1;
}
}
return -list.size() - 1;
}
- int low = 0, mid = list.size(), high = mid - 1, result = -1;
+ int low = 0, mid = list.size(), high = mid - 1, result = 1;
while (low <= high) {
mid = (low + high) >>> 1;
- if ((result = -comparator.compare(list.get(mid), object)) > 0) {
+ result = comparator.compare(list.get(mid), object);
+ if (result < 0) {
low = mid + 1;
} else if (result == 0) {
return mid;
@@ -1499,7 +1503,7 @@ public class Collections {
high = mid - 1;
}
}
- return -mid - (result < 0 ? 1 : 2);
+ return -mid - (result > 0 ? 1 : 2);
}
/**
@@ -1860,13 +1864,23 @@ public class Collections {
*/
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> void sort(List<T> list) {
- Object[] array = list.toArray();
- Arrays.sort(array);
- int i = 0;
- ListIterator<T> it = list.listIterator();
- while (it.hasNext()) {
- it.next();
- it.set((T) array[i++]);
+ // Note that we can't use instanceof here since ArrayList isn't final and
+ // subclasses might make arbitrary use of array and modCount.
+ if (list.getClass() == ArrayList.class) {
+ ArrayList<T> arrayList = (ArrayList<T>) list;
+ Object[] array = arrayList.array;
+ int end = arrayList.size();
+ Arrays.sort(array, 0, end);
+ arrayList.modCount++;
+ } else {
+ Object[] array = list.toArray();
+ Arrays.sort(array);
+ int i = 0;
+ ListIterator<T> it = list.listIterator();
+ while (it.hasNext()) {
+ it.next();
+ it.set((T) array[i++]);
+ }
}
}
@@ -1879,13 +1893,21 @@ public class Collections {
*/
@SuppressWarnings("unchecked")
public static <T> void sort(List<T> list, Comparator<? super T> comparator) {
- T[] array = list.toArray((T[]) new Object[list.size()]);
- Arrays.sort(array, comparator);
- int i = 0;
- ListIterator<T> it = list.listIterator();
- while (it.hasNext()) {
- it.next();
- it.set(array[i++]);
+ if (list.getClass() == ArrayList.class) {
+ ArrayList<T> arrayList = (ArrayList<T>) list;
+ T[] array = (T[]) arrayList.array;
+ int end = arrayList.size();
+ Arrays.sort(array, 0, end, comparator);
+ arrayList.modCount++;
+ } else {
+ T[] array = list.toArray((T[]) new Object[list.size()]);
+ Arrays.sort(array, comparator);
+ int i = 0;
+ ListIterator<T> it = list.listIterator();
+ while (it.hasNext()) {
+ it.next();
+ it.set(array[i++]);
+ }
}
}
diff --git a/luni/src/main/java/java/util/Date.java b/luni/src/main/java/java/util/Date.java
index b4de055..d45c971 100644
--- a/luni/src/main/java/java/util/Date.java
+++ b/luni/src/main/java/java/util/Date.java
@@ -47,7 +47,10 @@ public class Date implements Serializable, Cloneable, Comparable<Date> {
private static final long serialVersionUID = 7523967970034938905L;
// Used by parse()
- private static final int CREATION_YEAR = new Date().getYear();
+ // Keep in a static inner class to allow compile-time initialization of Date.
+ private static class CreationYear {
+ private static final int VALUE = new Date().getYear();
+ }
private transient long milliseconds;
@@ -539,7 +542,7 @@ public class Date implements Serializable, Cloneable, Comparable<Date> {
if (second == -1) {
second = 0;
}
- if (year < (CREATION_YEAR - 80)) {
+ if (year < (CreationYear.VALUE - 80)) {
year += 2000;
} else if (year < 100) {
year += 1900;
diff --git a/luni/src/main/java/java/util/EnumMap.java b/luni/src/main/java/java/util/EnumMap.java
index dfacb46..eee6ff4 100644
--- a/luni/src/main/java/java/util/EnumMap.java
+++ b/luni/src/main/java/java/util/EnumMap.java
@@ -36,9 +36,9 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
private Class<K> keyType;
- transient Enum[] keys;
+ transient K[] keys;
- transient Object[] values;
+ transient V[] values;
transient boolean[] hasMapping;
@@ -48,8 +48,7 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
private transient EnumMapEntrySet<K, V> entrySet = null;
- private static class Entry<KT extends Enum<KT>, VT> extends
- MapEntry<KT, VT> {
+ private static class Entry<KT extends Enum<KT>, VT> extends MapEntry<KT, VT> {
private final EnumMap<KT, VT> enumMap;
private final int ordinal;
@@ -57,10 +56,9 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
Entry(KT theKey, VT theValue, EnumMap<KT, VT> em) {
super(theKey, theValue);
enumMap = em;
- ordinal = ((Enum) theKey).ordinal();
+ ordinal = theKey.ordinal();
}
- @SuppressWarnings("unchecked")
@Override
public boolean equals(Object object) {
if (!enumMap.hasMapping[ordinal]) {
@@ -68,7 +66,7 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
}
boolean isEqual = false;
if (object instanceof Map.Entry) {
- Map.Entry<KT, VT> entry = (Map.Entry<KT, VT>) object;
+ Map.Entry<?, ?> entry = (Map.Entry<?, ?>) object;
Object enumKey = entry.getKey();
if (key.equals(enumKey)) {
Object theValue = entry.getValue();
@@ -84,37 +82,32 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
@Override
public int hashCode() {
- return (enumMap.keys[ordinal] == null ? 0 : enumMap.keys[ordinal]
- .hashCode())
+ return (enumMap.keys[ordinal] == null ? 0 : enumMap.keys[ordinal].hashCode())
^ (enumMap.values[ordinal] == null ? 0
: enumMap.values[ordinal].hashCode());
}
- @SuppressWarnings("unchecked")
@Override
public KT getKey() {
checkEntryStatus();
- return (KT) enumMap.keys[ordinal];
+ return enumMap.keys[ordinal];
}
- @SuppressWarnings("unchecked")
@Override
public VT getValue() {
checkEntryStatus();
- return (VT) enumMap.values[ordinal];
+ return enumMap.values[ordinal];
}
- @SuppressWarnings("unchecked")
@Override
public VT setValue(VT value) {
checkEntryStatus();
- return enumMap.put((KT) enumMap.keys[ordinal], value);
+ return enumMap.put(enumMap.keys[ordinal], value);
}
@Override
public String toString() {
- StringBuilder result = new StringBuilder(enumMap.keys[ordinal]
- .toString());
+ StringBuilder result = new StringBuilder(enumMap.keys[ordinal].toString());
result.append("=");
result.append(enumMap.values[ordinal] == null
? "null" : enumMap.values[ordinal].toString());
@@ -128,8 +121,7 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
}
}
- private static class EnumMapIterator<E, KT extends Enum<KT>, VT> implements
- Iterator<E> {
+ private static class EnumMapIterator<E, KT extends Enum<KT>, VT> implements Iterator<E> {
int position = 0;
int prePosition = -1;
@@ -153,13 +145,12 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
return position != length;
}
- @SuppressWarnings("unchecked")
public E next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
prePosition = position++;
- return type.get(new MapEntry(enumMap.keys[prePosition],
+ return type.get(new MapEntry<KT, VT>(enumMap.keys[prePosition],
enumMap.values[prePosition]));
}
@@ -172,13 +163,12 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
}
@Override
- @SuppressWarnings("unchecked")
public String toString() {
if (prePosition == -1) {
return super.toString();
}
return type.get(
- new MapEntry(enumMap.keys[prePosition],
+ new MapEntry<KT, VT>(enumMap.keys[prePosition],
enumMap.values[prePosition])).toString();
}
@@ -208,8 +198,7 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
}
@Override
- @SuppressWarnings("unchecked")
- public Iterator iterator() {
+ public Iterator<KT> iterator() {
return new EnumMapIterator<KT, KT, VT>(
new MapEntry.Type<KT, KT, VT>() {
public KT get(MapEntry<KT, VT> entry) {
@@ -219,7 +208,6 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
}
@Override
- @SuppressWarnings("unchecked")
public boolean remove(Object object) {
if (contains(object)) {
enumMap.remove(object);
@@ -252,9 +240,8 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
return enumMap.containsValue(object);
}
- @SuppressWarnings("unchecked")
@Override
- public Iterator iterator() {
+ public Iterator<VT> iterator() {
return new EnumMapIterator<VT, KT, VT>(
new MapEntry.Type<VT, KT, VT>() {
public VT get(MapEntry<KT, VT> entry) {
@@ -296,15 +283,14 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
super(value, em);
}
- @SuppressWarnings("unchecked")
@Override
public E next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
prePosition = position++;
- return type.get(new Entry<KT, VT>((KT) enumMap.keys[prePosition],
- (VT) enumMap.values[prePosition], enumMap));
+ return type.get(new EnumMap.Entry<KT, VT>(enumMap.keys[prePosition],
+ enumMap.values[prePosition], enumMap));
}
}
@@ -325,8 +311,8 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
public boolean contains(Object object) {
boolean isEqual = false;
if (object instanceof Map.Entry) {
- Object enumKey = ((Map.Entry) object).getKey();
- Object enumValue = ((Map.Entry) object).getValue();
+ Object enumKey = ((Map.Entry<?, ?>) object).getKey();
+ Object enumValue = ((Map.Entry<?, ?>) object).getValue();
if (enumMap.containsKey(enumKey)) {
VT value = enumMap.get(enumKey);
if (value == null) {
@@ -352,7 +338,7 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
@Override
public boolean remove(Object object) {
if (contains(object)) {
- enumMap.remove(((Map.Entry) object).getKey());
+ enumMap.remove(((Map.Entry<?, ?>) object).getKey());
return true;
}
return false;
@@ -369,21 +355,22 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
return toArray(entryArray);
}
- @SuppressWarnings("unchecked")
@Override
- public Object[] toArray(Object[] array) {
+ public <T> T[] toArray(T[] array) {
int size = enumMap.size();
int index = 0;
- Object[] entryArray = array;
+ T[] entryArray = array;
if (size > array.length) {
Class<?> clazz = array.getClass().getComponentType();
- entryArray = (Object[]) Array.newInstance(clazz, size);
+ @SuppressWarnings("unchecked") T[] newArray = (T[]) Array.newInstance(clazz, size);
+ entryArray = newArray;
}
Iterator<Map.Entry<KT, VT>> iter = iterator();
for (; index < size; index++) {
Map.Entry<KT, VT> entry = iter.next();
- entryArray[index] = new MapEntry<KT, VT>(entry.getKey(), entry
- .getValue());
+ @SuppressWarnings("unchecked") T newEntry =
+ (T) new MapEntry<KT, VT>(entry.getKey(), entry.getValue());
+ entryArray[index] = newEntry;
}
if (index < array.length) {
entryArray[index] = null;
@@ -431,22 +418,27 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
* @throws NullPointerException
* if {@code map} is {@code null}.
*/
- @SuppressWarnings("unchecked")
public EnumMap(Map<K, ? extends V> map) {
if (map instanceof EnumMap) {
- initialization((EnumMap<K, V>) map);
+ @SuppressWarnings("unchecked") EnumMap<K, ? extends V> enumMap =
+ (EnumMap<K, ? extends V>) map;
+ initialization(enumMap);
} else {
if (map.isEmpty()) {
throw new IllegalArgumentException("map is empty");
}
Iterator<K> iter = map.keySet().iterator();
K enumKey = iter.next();
- Class clazz = enumKey.getClass();
- if (clazz.isEnum()) {
- initialization(clazz);
- } else {
- initialization(clazz.getSuperclass());
+ // Confirm the key is actually an enum: Throw ClassCastException if not.
+ Enum.class.cast(enumKey);
+ Class<?> clazz = enumKey.getClass();
+ if (!clazz.isEnum()) {
+ // Each enum value can have its own subclass. In this case we want the abstract
+ // super-class which has the values() method.
+ clazz = clazz.getSuperclass();
}
+ @SuppressWarnings("unchecked") Class<K> enumClass = (Class<K>) clazz;
+ initialization(enumClass);
putAllImpl(map);
}
}
@@ -469,11 +461,10 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
*
* @return a shallow copy of this {@code EnumMap}.
*/
- @SuppressWarnings("unchecked")
@Override
public EnumMap<K, V> clone() {
try {
- EnumMap<K, V> enumMap = (EnumMap<K, V>) super.clone();
+ @SuppressWarnings("unchecked") EnumMap<K, V> enumMap = (EnumMap<K, V>) super.clone();
enumMap.initialization(this);
return enumMap;
} catch (CloneNotSupportedException e) {
@@ -553,7 +544,6 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
* @see #hashCode()
* @see #entrySet()
*/
- @SuppressWarnings("unchecked")
@Override
public boolean equals(Object object) {
if (this == object) {
@@ -562,7 +552,7 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
if (!(object instanceof EnumMap)) {
return super.equals(object);
}
- EnumMap<K, V> enumMap = (EnumMap<K, V>) object;
+ @SuppressWarnings("unchecked") EnumMap<K, V> enumMap = (EnumMap<K, V>) object;
if (keyType != enumMap.keyType || size() != enumMap.size()) {
return false;
}
@@ -579,13 +569,12 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
* if no mapping for the specified key is found.
*/
@Override
- @SuppressWarnings("unchecked")
public V get(Object key) {
if (!isValidKeyType(key)) {
return null;
}
int keyOrdinal = ((Enum) key).ordinal();
- return (V) values[keyOrdinal];
+ return values[keyOrdinal];
}
/**
@@ -627,7 +616,6 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
* support {@code null} keys or values.
*/
@Override
- @SuppressWarnings("unchecked")
public V put(K key, V value) {
return putImpl(key, value);
}
@@ -649,7 +637,6 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
* support {@code null} keys or values.
*/
@Override
- @SuppressWarnings("unchecked")
public void putAll(Map<? extends K, ? extends V> map) {
putAllImpl(map);
}
@@ -665,7 +652,6 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
* if removing from this {@code EnumMap} is not supported.
*/
@Override
- @SuppressWarnings("unchecked")
public V remove(Object key) {
if (!isValidKeyType(key)) {
return null;
@@ -675,7 +661,7 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
hasMapping[keyOrdinal] = false;
mappingsCount--;
}
- V oldValue = (V) values[keyOrdinal];
+ V oldValue = values[keyOrdinal];
values[keyOrdinal] = null;
return oldValue;
}
@@ -716,35 +702,29 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
stream.defaultReadObject();
initialization(keyType);
int elementCount = stream.readInt();
- Enum<K> enumKey;
- Object value;
+ K enumKey;
+ V value;
for (int i = elementCount; i > 0; i--) {
- enumKey = (Enum<K>) stream.readObject();
- value = stream.readObject();
- putImpl((K) enumKey, (V) value);
+ enumKey = (K) stream.readObject();
+ value = (V) stream.readObject();
+ putImpl(enumKey, value);
}
}
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
stream.writeInt(mappingsCount);
- Iterator<Map.Entry<K, V>> iterator = entrySet().iterator();
- while (iterator.hasNext()) {
- Map.Entry<K, V> entry = iterator.next();
+ for (Map.Entry<K, V> entry : entrySet()) {
stream.writeObject(entry.getKey());
stream.writeObject(entry.getValue());
}
}
private boolean isValidKeyType(Object key) {
- if (key != null && keyType.isInstance(key)) {
- return true;
- }
- return false;
+ return key != null && keyType.isInstance(key);
}
- @SuppressWarnings("unchecked")
- private void initialization(EnumMap enumMap) {
+ private void initialization(EnumMap<K, ? extends V> enumMap) {
keyType = enumMap.keyType;
keys = enumMap.keys;
enumSize = enumMap.enumSize;
@@ -757,20 +737,19 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
keyType = type;
keys = Enum.getSharedConstants(keyType);
enumSize = keys.length;
- values = new Object[enumSize];
+ // The value array is actually Object[] for speed of creation. It is treated as a V[]
+ // because it is safe to do so and eliminates unchecked warning suppression throughout.
+ @SuppressWarnings("unchecked") V[] valueArray = (V[]) new Object[enumSize];
+ values = valueArray;
hasMapping = new boolean[enumSize];
}
- @SuppressWarnings("unchecked")
- private void putAllImpl(Map map) {
- Iterator iter = map.entrySet().iterator();
- while (iter.hasNext()) {
- Map.Entry entry = (Map.Entry) iter.next();
- putImpl((K) entry.getKey(), (V) entry.getValue());
+ private void putAllImpl(Map<? extends K, ? extends V> map) {
+ for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
+ putImpl(entry.getKey(), entry.getValue());
}
}
- @SuppressWarnings("unchecked")
private V putImpl(K key, V value) {
if (key == null) {
throw new NullPointerException("key == null");
@@ -781,7 +760,7 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements
hasMapping[keyOrdinal] = true;
mappingsCount++;
}
- V oldValue = (V) values[keyOrdinal];
+ V oldValue = values[keyOrdinal];
values[keyOrdinal] = value;
return oldValue;
}
diff --git a/luni/src/main/java/java/util/Locale.java b/luni/src/main/java/java/util/Locale.java
index a6368e8..1885f83 100644
--- a/luni/src/main/java/java/util/Locale.java
+++ b/luni/src/main/java/java/util/Locale.java
@@ -272,17 +272,77 @@ public final class Locale implements Cloneable, Serializable {
*/
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.
+ * Map of grandfathered language tags to their modern replacements.
*/
- private static Locale defaultLocale = US;
+ private static final TreeMap<String, String> GRANDFATHERED_LOCALES;
static {
- String language = System.getProperty("user.language", "en");
- String region = System.getProperty("user.region", "US");
- String variant = System.getProperty("user.variant", "");
- defaultLocale = new Locale(language, region, variant);
+ 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 class NoImagePreloadHolder {
+ /**
+ * The default locale, returned by {@code Locale.getDefault()}.
+ * Initialize the default locale from the system properties.
+ */
+ private static Locale defaultLocale = Locale.getDefaultLocaleFromSystemProperties();
+ }
+
+ /**
+ * Returns the default locale from system properties.
+ *
+ * @hide visible for testing.
+ */
+ public static Locale getDefaultLocaleFromSystemProperties() {
+ final String languageTag = System.getProperty("user.locale", "");
+
+ final Locale defaultLocale;
+ if (!languageTag.isEmpty()) {
+ defaultLocale = Locale.forLanguageTag(languageTag);
+ } else {
+ String language = System.getProperty("user.language", "en");
+ String region = System.getProperty("user.region", "US");
+ String variant = System.getProperty("user.variant", "");
+ defaultLocale = new Locale(language, region, variant);
+ }
+
+ return defaultLocale;
}
/**
@@ -1006,7 +1066,7 @@ public final class Locale implements Cloneable, Serializable {
* Instead, use this method to look it up for each use.
*/
public static Locale getDefault() {
- return defaultLocale;
+ return NoImagePreloadHolder.defaultLocale;
}
/**
@@ -1600,7 +1660,7 @@ public final class Locale implements Cloneable, Serializable {
throw new NullPointerException("locale == null");
}
String languageTag = locale.toLanguageTag();
- defaultLocale = locale;
+ NoImagePreloadHolder.defaultLocale = locale;
ICU.setDefaultLocale(languageTag);
}
@@ -2020,49 +2080,6 @@ public final class Locale implements Cloneable, Serializable {
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;
diff --git a/luni/src/main/java/java/util/TimeZone.java b/luni/src/main/java/java/util/TimeZone.java
index 854a4a6..d7beb91 100644
--- a/luni/src/main/java/java/util/TimeZone.java
+++ b/luni/src/main/java/java/util/TimeZone.java
@@ -68,7 +68,51 @@ import org.apache.harmony.luni.internal.util.TimezoneGetter;
public abstract class TimeZone implements Serializable, Cloneable {
private static final long serialVersionUID = 3581463369166924961L;
- private static final Pattern CUSTOM_ZONE_ID_PATTERN = Pattern.compile("^GMT[-+](\\d{1,2})(:?(\\d\\d))?$");
+ /**
+ * Helper class to parse a custom timezone. This is in a separate class as regular expressions
+ * cannot be compile-time initialized, so that static field is separated out from TimeZone
+ * proper.
+ */
+ private final static class CustomTimeZoneParser {
+ private static final Pattern CUSTOM_ZONE_ID_PATTERN =
+ Pattern.compile("^GMT[-+](\\d{1,2})(:?(\\d\\d))?$");
+
+ private CustomTimeZoneParser() {}
+
+ /**
+ * Returns a new SimpleTimeZone for an ID of the form "GMT[+|-]hh[[:]mm]", or null.
+ */
+ private static TimeZone getCustomTimeZone(String id) {
+ Matcher m = CUSTOM_ZONE_ID_PATTERN.matcher(id);
+ if (!m.matches()) {
+ return null;
+ }
+
+ int hour;
+ int minute = 0;
+ try {
+ hour = Integer.parseInt(m.group(1));
+ if (m.group(3) != null) {
+ minute = Integer.parseInt(m.group(3));
+ }
+ } catch (NumberFormatException impossible) {
+ throw new AssertionError(impossible);
+ }
+
+ if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
+ return null;
+ }
+
+ char sign = id.charAt(3);
+ int raw = (hour * 3600000) + (minute * 60000);
+ if (sign == '-') {
+ raw = -raw;
+ }
+
+ String cleanId = String.format((Locale) null, "GMT%c%02d:%02d", sign, hour, minute);
+ return new SimpleTimeZone(raw, cleanId);
+ }
+ }
/**
* The short display name style, such as {@code PDT}. Requests for this
@@ -368,7 +412,7 @@ public abstract class TimeZone implements Serializable, Cloneable {
// Custom time zone?
if (zone == null && id.length() > 3 && id.startsWith("GMT")) {
- zone = getCustomTimeZone(id);
+ zone = CustomTimeZoneParser.getCustomTimeZone(id);
}
// We never return null; on failure we return the equivalent of "GMT".
@@ -376,40 +420,6 @@ public abstract class TimeZone implements Serializable, Cloneable {
}
/**
- * Returns a new SimpleTimeZone for an ID of the form "GMT[+|-]hh[[:]mm]", or null.
- */
- private static TimeZone getCustomTimeZone(String id) {
- Matcher m = CUSTOM_ZONE_ID_PATTERN.matcher(id);
- if (!m.matches()) {
- return null;
- }
-
- int hour;
- int minute = 0;
- try {
- hour = Integer.parseInt(m.group(1));
- if (m.group(3) != null) {
- minute = Integer.parseInt(m.group(3));
- }
- } catch (NumberFormatException impossible) {
- throw new AssertionError(impossible);
- }
-
- if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
- return null;
- }
-
- char sign = id.charAt(3);
- int raw = (hour * 3600000) + (minute * 60000);
- if (sign == '-') {
- raw = -raw;
- }
-
- String cleanId = String.format("GMT%c%02d:%02d", sign, hour, minute);
- return new SimpleTimeZone(raw, cleanId);
- }
-
- /**
* Returns true if {@code timeZone} has the same rules as this time zone.
*
* <p>The base implementation returns true if both time zones have the same
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
index 803cd49..4a76104 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
@@ -293,11 +293,13 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
private static final long serialVersionUID = -8627078645895051609L;
- /**
- * Generates the initial random seed for the cheaper per-instance
- * random number generators used in randomLevel.
- */
- private static final Random seedGenerator = new Random();
+// BEGIN android-removed
+// /**
+// * Generates the initial random seed for the cheaper per-instance
+// * random number generators used in randomLevel.
+// */
+// private static final Random seedGenerator = new Random();
+// END android-removed
/**
* Special value used to identify base-level header
@@ -341,7 +343,13 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
entrySet = null;
values = null;
descendingMap = null;
- randomSeed = seedGenerator.nextInt() | 0x0100; // ensure nonzero
+ // BEGIN android-changed
+ //
+ // Most processes are forked from the zygote, so they'll end up
+ // with the same random seed unless we take additional post fork
+ // measures.
+ randomSeed = Math.randomIntInternal() | 0x0100; // ensure nonzero
+ // END android-changed
head = new HeadIndex<K,V>(new Node<K,V>(null, BASE_HEADER, null),
null, null, 1);
}
diff --git a/luni/src/main/java/java/util/jar/Manifest.java b/luni/src/main/java/java/util/jar/Manifest.java
index 6a3936d..5a6b42d 100644
--- a/luni/src/main/java/java/util/jar/Manifest.java
+++ b/luni/src/main/java/java/util/jar/Manifest.java
@@ -41,8 +41,10 @@ public class Manifest implements Cloneable {
private static final byte[] VALUE_SEPARATOR = new byte[] { ':', ' ' };
- private final Attributes mainAttributes;
- private final HashMap<String, Attributes> entries;
+ /* non-final for {@code #clone()} */
+ private Attributes mainAttributes;
+ /* non-final for {@code #clone()} */
+ private HashMap<String, Attributes> entries;
static final class Chunk {
final int start;
@@ -93,9 +95,7 @@ public class Manifest implements Cloneable {
*/
@SuppressWarnings("unchecked")
public Manifest(Manifest man) {
- mainAttributes = (Attributes) man.mainAttributes.clone();
- entries = (HashMap<String, Attributes>) ((HashMap<String, Attributes>) man
- .getEntries()).clone();
+ cloneAttributesAndEntriesFrom(man);
}
Manifest(byte[] manifestBytes, boolean readChunks) throws IOException {
@@ -156,7 +156,21 @@ public class Manifest implements Cloneable {
*/
@Override
public Object clone() {
- return new Manifest(this);
+ Manifest result;
+ try {
+ result = (Manifest) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(e);
+ }
+
+ result.cloneAttributesAndEntriesFrom(this);
+ return result;
+ }
+
+ private final void cloneAttributesAndEntriesFrom(Manifest other) {
+ mainAttributes = (Attributes) other.mainAttributes.clone();
+ entries = (HashMap<String, Attributes>) ((HashMap<String, Attributes>) other
+ .getEntries()).clone();
}
/**
diff --git a/luni/src/main/java/java/util/jar/StrictJarFile.java b/luni/src/main/java/java/util/jar/StrictJarFile.java
index 4a8af5f..a73ca2a 100644
--- a/luni/src/main/java/java/util/jar/StrictJarFile.java
+++ b/luni/src/main/java/java/util/jar/StrictJarFile.java
@@ -24,6 +24,7 @@ import java.io.RandomAccessFile;
import java.security.cert.Certificate;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.Set;
import java.util.zip.Inflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -53,7 +54,7 @@ public final class StrictJarFile {
private final CloseGuard guard = CloseGuard.get();
private boolean closed;
- public StrictJarFile(String fileName) throws IOException {
+ public StrictJarFile(String fileName) throws IOException, SecurityException {
this.nativeHandle = nativeOpenJarFile(fileName);
this.raf = new RandomAccessFile(fileName, "r");
@@ -64,11 +65,18 @@ public final class StrictJarFile {
HashMap<String, byte[]> metaEntries = getMetaEntries();
this.manifest = new Manifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
this.verifier = new JarVerifier(fileName, manifest, metaEntries);
+ Set<String> files = this.manifest.getEntries().keySet();
+ for (String file : files) {
+ if (findEntry(file) == null) {
+ throw new SecurityException(fileName + ": File " + file + " in manifest does not exist");
+ }
+ }
isSigned = verifier.readCertificates() && verifier.isSignedJar();
- } catch (IOException ioe) {
+ } catch (IOException | SecurityException e) {
nativeClose(this.nativeHandle);
- throw ioe;
+ IoUtils.closeQuietly(this.raf);
+ throw e;
}
guard.open("close");
diff --git a/luni/src/main/java/java/util/logging/FileHandler.java b/luni/src/main/java/java/util/logging/FileHandler.java
index 6ffef87..1fd1dbf 100644
--- a/luni/src/main/java/java/util/logging/FileHandler.java
+++ b/luni/src/main/java/java/util/logging/FileHandler.java
@@ -261,15 +261,15 @@ public class FileHandler extends StreamHandler {
boolean hasUniqueID = false;
boolean hasGeneration = false;
- // TODO privilege code?
+ String homePath = System.getProperty("user.home");
+ if (homePath == null) {
+ throw new NullPointerException("System property \"user.home\" is null");
+ }
+ boolean homePathHasSepEnd = homePath.endsWith(File.separator);
String tempPath = System.getProperty("java.io.tmpdir");
- boolean tempPathHasSepEnd = (tempPath == null ? false : tempPath
- .endsWith(File.separator));
-
- String homePath = System.getProperty("user.home");
- boolean homePathHasSepEnd = (homePath == null ? false : homePath
- .endsWith(File.separator));
+ tempPath = tempPath == null ? homePath : tempPath;
+ boolean tempPathHasSepEnd = tempPath.endsWith(File.separator);
StringBuilder sb = new StringBuilder();
pattern = pattern.replace('/', File.separatorChar);
diff --git a/luni/src/main/java/java/util/logging/SocketHandler.java b/luni/src/main/java/java/util/logging/SocketHandler.java
index 48bfc0e..5de847a 100644
--- a/luni/src/main/java/java/util/logging/SocketHandler.java
+++ b/luni/src/main/java/java/util/logging/SocketHandler.java
@@ -17,6 +17,7 @@
package java.util.logging;
+import libcore.net.NetworkSecurityPolicy;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.Socket;
@@ -106,14 +107,21 @@ public class SocketHandler extends StreamHandler {
throw new IllegalArgumentException("host == null || host.isEmpty()");
}
// check the validity of the port number
- int p = 0;
+ int p;
try {
p = Integer.parsePositiveInt(port);
+ // Must be >= 0 to get this far. 0 is invalid too.
+ if (p == 0) {
+ throw new IllegalArgumentException("Illegal port argument " + port);
+ }
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Illegal port argument " + port);
}
// establish the network connection
try {
+ if (!NetworkSecurityPolicy.isCleartextTrafficPermitted()) {
+ throw new IOException("Cleartext traffic not permitted");
+ }
this.socket = new Socket(host, p);
} catch (IOException e) {
getErrorManager().error("Failed to establish the network connection", e,
diff --git a/luni/src/main/java/java/util/logging/XMLFormatter.java b/luni/src/main/java/java/util/logging/XMLFormatter.java
index 0d80b3e..3952596 100644
--- a/luni/src/main/java/java/util/logging/XMLFormatter.java
+++ b/luni/src/main/java/java/util/logging/XMLFormatter.java
@@ -17,6 +17,7 @@
package java.util.logging;
+import java.io.IOException;
import java.text.MessageFormat;
import java.util.Date;
import java.util.ResourceBundle;
@@ -50,7 +51,7 @@ public class XMLFormatter extends Formatter {
// call a method of LogRecord to ensure not null
long time = r.getMillis();
// format to date
- String date = MessageFormat.format("{0, date} {0, time}", new Object[] { new Date(time) });
+ String date = MessageFormat.format("{0, date} {0, time}", new Date(time));
String nl = System.lineSeparator();
StringBuilder sb = new StringBuilder();
@@ -59,21 +60,21 @@ public class XMLFormatter extends Formatter {
append(sb, 1, "millis", time);
append(sb, 1, "sequence", r.getSequenceNumber());
if (r.getLoggerName() != null) {
- append(sb, 1, "logger", r.getLoggerName());
+ escapeAndAppend(sb, 1, "logger", r.getLoggerName());
}
append(sb, 1, "level", r.getLevel().getName());
if (r.getSourceClassName() != null) {
append(sb, 1, "class", r.getSourceClassName());
}
if (r.getSourceMethodName() != null) {
- append(sb, 1, "method", r.getSourceMethodName());
+ escapeAndAppend(sb, 1, "method", r.getSourceMethodName());
}
append(sb, 1, "thread", r.getThreadID());
formatMessages(r, sb);
Object[] params = r.getParameters();
if (params != null) {
for (Object element : params) {
- append(sb, 1, "param", element);
+ escapeAndAppend(sb, 1, "param", element);
}
}
formatThrowable(r, sb);
@@ -96,14 +97,14 @@ public class XMLFormatter extends Formatter {
if (message == null) {
message = pattern;
- append(sb, 1, "message", message);
+ escapeAndAppend(sb, 1, "message", message);
} else {
- append(sb, 1, "message", message);
- append(sb, 1, "key", pattern);
- append(sb, 1, "catalog", r.getResourceBundleName());
+ escapeAndAppend(sb, 1, "message", message);
+ escapeAndAppend(sb, 1, "key", pattern);
+ escapeAndAppend(sb, 1, "catalog", r.getResourceBundleName());
}
} else if (pattern != null) {
- append(sb, 1, "message", pattern);
+ escapeAndAppend(sb, 1, "message", pattern);
} else {
sb.append(indent).append("<message/>");
}
@@ -114,13 +115,13 @@ public class XMLFormatter extends Formatter {
if ((t = r.getThrown()) != null) {
String nl = System.lineSeparator();
sb.append(indent).append("<exception>").append(nl);
- append(sb, 2, "message", t.toString());
+ escapeAndAppend(sb, 2, "message", t.toString());
// format throwable's stack trace
StackTraceElement[] elements = t.getStackTrace();
for (StackTraceElement e : elements) {
sb.append(indent).append(indent).append("<frame>").append(nl);
append(sb, 3, "class", e.getClassName());
- append(sb, 3, "method", e.getMethodName());
+ escapeAndAppend(sb, 3, "method", e.getMethodName());
append(sb, 3, "line", e.getLineNumber());
sb.append(indent).append(indent).append("</frame>").append(nl);
}
@@ -138,6 +139,49 @@ public class XMLFormatter extends Formatter {
sb.append(System.lineSeparator());
}
+ private static void escapeAndAppend(StringBuilder sb, int indentCount, String tag, Object value) {
+ if (value == null) {
+ append(sb, indentCount, tag, value);
+ } else {
+ for (int i = 0; i < indentCount; ++i) {
+ sb.append(indent);
+ }
+ sb.append("<").append(tag).append(">");
+ try {
+ escapeXml(sb, value.toString());
+ } catch (IOException e) {
+ throw new AssertionError();
+ }
+ sb.append("</").append(tag).append(">");
+ sb.append(System.lineSeparator());
+ }
+ }
+
+ private static void escapeXml(Appendable valueBuilder, String value) throws IOException {
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+ switch (c) {
+ case '\"':
+ valueBuilder.append("&quot;");
+ break;
+ case '>':
+ valueBuilder.append("&gt;");
+ break;
+ case '<':
+ valueBuilder.append("&lt;");
+ break;
+ case '&':
+ valueBuilder.append("&amp;");
+ break;
+ case '\'':
+ valueBuilder.append("&apos;");
+ break;
+ default:
+ valueBuilder.append(c);
+ }
+ }
+ }
+
/**
* Returns the header string for a set of log records formatted as XML
* strings, using the output handler's encoding if it is defined, otherwise
diff --git a/luni/src/main/java/java/util/zip/GZIPInputStream.java b/luni/src/main/java/java/util/zip/GZIPInputStream.java
index 1bfc496..925e8c4 100644
--- a/luni/src/main/java/java/util/zip/GZIPInputStream.java
+++ b/luni/src/main/java/java/util/zip/GZIPInputStream.java
@@ -222,7 +222,7 @@ public class GZIPInputStream extends InflaterInputStream {
if (hcrc) {
crc.update(header, 0, 2);
}
- int length = Memory.peekShort(scratch, 0, ByteOrder.LITTLE_ENDIAN) & 0xffff;
+ int length = Memory.peekShort(header, 0, ByteOrder.LITTLE_ENDIAN) & 0xffff;
while (length > 0) {
int max = length > scratch.length ? scratch.length : length;
int result = in.read(scratch, 0, max);
@@ -243,7 +243,7 @@ public class GZIPInputStream extends InflaterInputStream {
}
if (hcrc) {
Streams.readFully(in, header, 0, 2);
- short crc16 = Memory.peekShort(scratch, 0, ByteOrder.LITTLE_ENDIAN);
+ short crc16 = Memory.peekShort(header, 0, ByteOrder.LITTLE_ENDIAN);
if ((short) crc.getValue() != crc16) {
throw new IOException("CRC mismatch");
}
diff --git a/luni/src/main/java/java/util/zip/Zip64.java b/luni/src/main/java/java/util/zip/Zip64.java
new file mode 100644
index 0000000..9be3d1c
--- /dev/null
+++ b/luni/src/main/java/java/util/zip/Zip64.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package java.util.zip;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import static java.util.zip.ZipOutputStream.writeIntAsUint16;
+import static java.util.zip.ZipOutputStream.writeLongAsUint32;
+import static java.util.zip.ZipOutputStream.writeLongAsUint64;
+
+/**
+ * @hide
+ */
+public class Zip64 {
+
+ /* Non instantiable */
+ private Zip64() {}
+
+ /**
+ * The maximum supported entry / archive size for standard (non zip64) entries and archives.
+ */
+ static final long MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE = 0x00000000ffffffffL;
+
+ /**
+ * The header ID of the zip64 extended info header. This value is used to identify
+ * zip64 data in the "extra" field in the file headers.
+ */
+ private static final short ZIP64_EXTENDED_INFO_HEADER_ID = 0x0001;
+
+ /**
+ * The minimum size of the zip64 extended info header. This excludes the 2 byte header ID
+ * and the 2 byte size.
+ */
+ private static final int ZIP64_EXTENDED_INFO_MIN_SIZE = 28;
+
+ /*
+ * Size (in bytes) of the zip64 end of central directory locator. This will be located
+ * immediately before the end of central directory record if a given zipfile is in the
+ * zip64 format.
+ */
+ private static final int ZIP64_LOCATOR_SIZE = 20;
+
+ /**
+ * The zip64 end of central directory locator signature (4 bytes wide).
+ */
+ private static final int ZIP64_LOCATOR_SIGNATURE = 0x07064b50;
+
+ /**
+ * The zip64 end of central directory record singature (4 bytes wide).
+ */
+ private static final int ZIP64_EOCD_RECORD_SIGNATURE = 0x06064b50;
+
+ /**
+ * The "effective" size of the zip64 eocd record. This excludes the fields that
+ * are proprietary, signature, or fields we aren't interested in. We include the
+ * following (contiguous) fields in this calculation :
+ * - disk number (4 bytes)
+ * - disk with start of central directory (4 bytes)
+ * - number of central directory entries on this disk (8 bytes)
+ * - total number of central directory entries (8 bytes)
+ * - size of the central directory (8 bytes)
+ * - offset of the start of the central directory (8 bytes)
+ */
+ private static final int ZIP64_EOCD_RECORD_EFFECTIVE_SIZE = 40;
+
+ /**
+ * Parses the zip64 end of central directory record locator. The locator
+ * must be placed immediately before the end of central directory (eocd) record
+ * starting at {@code eocdOffset}.
+ *
+ * The position of the file cursor for {@code raf} after a call to this method
+ * is undefined an callers must reposition it after each call to this method.
+ */
+ public static long parseZip64EocdRecordLocator(RandomAccessFile raf, long eocdOffset)
+ throws IOException {
+ // The spec stays curiously silent about whether a zip file with an EOCD record,
+ // a zip64 locator and a zip64 eocd record is considered "empty". In our implementation,
+ // we parse all records and read the counts from them instead of drawing any size or
+ // layout based information.
+ if (eocdOffset > ZIP64_LOCATOR_SIZE) {
+ raf.seek(eocdOffset - ZIP64_LOCATOR_SIZE);
+ if (Integer.reverseBytes(raf.readInt()) == ZIP64_LOCATOR_SIGNATURE) {
+ byte[] zip64EocdLocator = new byte[ZIP64_LOCATOR_SIZE - 4];
+ raf.readFully(zip64EocdLocator);
+ ByteBuffer buf = ByteBuffer.wrap(zip64EocdLocator).order(ByteOrder.LITTLE_ENDIAN);
+
+ final int diskWithCentralDir = buf.getInt();
+ final long zip64EocdRecordOffset = buf.getLong();
+ final int numDisks = buf.getInt();
+
+ if (numDisks != 1 || diskWithCentralDir != 0) {
+ throw new ZipException("Spanned archives not supported");
+ }
+
+ return zip64EocdRecordOffset;
+ }
+ }
+
+ return -1;
+ }
+
+ public static ZipFile.EocdRecord parseZip64EocdRecord(RandomAccessFile raf,
+ long eocdRecordOffset, int commentLength) throws IOException {
+ raf.seek(eocdRecordOffset);
+ final int signature = Integer.reverseBytes(raf.readInt());
+ if (signature != ZIP64_EOCD_RECORD_SIGNATURE) {
+ throw new ZipException("Invalid zip64 eocd record offset, sig="
+ + Integer.toHexString(signature) + " offset=" + eocdRecordOffset);
+ }
+
+ // The zip64 eocd record specifies its own size as an 8 byte integral type. It is variable
+ // length because of the "zip64 extensible data sector" but that field is reserved for
+ // pkware's proprietary use. We therefore disregard it altogether and treat the end of
+ // central directory structure as fixed length.
+ //
+ // We also skip "version made by" (2 bytes) and "version needed to extract" (2 bytes)
+ // fields. We perform additional validation at the ZipEntry level, where applicable.
+ //
+ // That's a total of 12 bytes to skip
+ raf.skipBytes(12);
+
+ byte[] zip64Eocd = new byte[ZIP64_EOCD_RECORD_EFFECTIVE_SIZE];
+ raf.readFully(zip64Eocd);
+
+ ByteBuffer buf = ByteBuffer.wrap(zip64Eocd).order(ByteOrder.LITTLE_ENDIAN);
+ try {
+ int diskNumber = buf.getInt();
+ int diskWithCentralDirStart = buf.getInt();
+ long numEntries = buf.getLong();
+ long totalNumEntries = buf.getLong();
+ buf.getLong(); // Ignore the size of the central directory
+ long centralDirOffset = buf.getLong();
+
+ if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDirStart != 0) {
+ throw new ZipException("Spanned archives not supported :" +
+ " numEntries=" + numEntries + ", totalNumEntries=" + totalNumEntries +
+ ", diskNumber=" + diskNumber + ", diskWithCentralDirStart=" +
+ diskWithCentralDirStart);
+ }
+
+ return new ZipFile.EocdRecord(numEntries, centralDirOffset, commentLength);
+ } catch (BufferUnderflowException bue) {
+ ZipException zipException = new ZipException("Error parsing zip64 eocd record.");
+ zipException.initCause(bue);
+ throw zipException;
+ }
+ }
+
+ /**
+ * Parse the zip64 extended info record from the extras present in {@code ze}.
+ *
+ * If {@code fromCentralDirectory} is true, we assume we're parsing a central directory
+ * record. We assume a local file header otherwise. The difference between the two is that
+ * a central directory entry is required to be complete, whereas a local file header isn't.
+ * This is due to the presence of an optional data descriptor after the file content.
+ *
+ * @return {@code} true iff. a zip64 extended info record was found.
+ */
+ public static boolean parseZip64ExtendedInfo(ZipEntry ze, boolean fromCentralDirectory)
+ throws ZipException {
+ int extendedInfoSize = -1;
+ int extendedInfoStart = -1;
+ // If this file contains a zip64 central directory locator, entries might
+ // optionally contain a zip64 extended information extra entry.
+ if (ze.extra != null && ze.extra.length > 0) {
+ // Extensible data fields are of the form header1+data1 + header2+data2 and so
+ // on, where each header consists of a 2 byte header ID followed by a 2 byte size.
+ // We need to iterate through the entire list of headers to find the header ID
+ // for the zip64 extended information extra field (0x0001).
+ final ByteBuffer buf = ByteBuffer.wrap(ze.extra).order(ByteOrder.LITTLE_ENDIAN);
+ extendedInfoSize = getZip64ExtendedInfoSize(buf);
+ if (extendedInfoSize != -1) {
+ extendedInfoStart = buf.position();
+ try {
+ if (extendedInfoSize < ZIP64_EXTENDED_INFO_MIN_SIZE) {
+ throw new ZipException("Invalid zip64 extended info size: " + extendedInfoSize);
+ }
+
+ // The size & compressed size only make sense in the central directory *or* if
+ // we know them beforehand. If we don't know them beforehand, they're stored in
+ // the data descriptor and should be read from there.
+ if (fromCentralDirectory || (ze.getMethod() == ZipEntry.STORED)) {
+ final long zip64Size = buf.getLong();
+ if (ze.size == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) {
+ ze.size = zip64Size;
+ }
+
+ final long zip64CompressedSize = buf.getLong();
+ if (ze.compressedSize == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) {
+ ze.compressedSize = zip64CompressedSize;
+ }
+ }
+
+ // The local header offset is significant only in the central directory. It makes no
+ // sense within the local header itself.
+ if (fromCentralDirectory) {
+ final long zip64LocalHeaderRelOffset = buf.getLong();
+ if (ze.localHeaderRelOffset == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) {
+ ze.localHeaderRelOffset = zip64LocalHeaderRelOffset;
+ }
+ }
+ } catch (BufferUnderflowException bue) {
+ ZipException zipException = new ZipException("Error parsing extendend info ");
+ zipException.initCause(bue);
+ throw zipException;
+ }
+ }
+ }
+
+ // This entry doesn't contain a zip64 extended information data entry header.
+ // We have to check that the compressedSize / size / localHeaderRelOffset values
+ // are valid and don't require the presence of the extended header.
+ if (extendedInfoSize == -1) {
+ if (ze.compressedSize == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE ||
+ ze.size == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE ||
+ ze.localHeaderRelOffset == MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) {
+ throw new ZipException("File contains no zip64 extended information: "
+ + "name=" + ze.name + "compressedSize=" + ze.compressedSize + ", size="
+ + ze.size + ", localHeader=" + ze.localHeaderRelOffset);
+ }
+
+ return false;
+ } else {
+ // If we're parsed the zip64 extended info header, we remove it from the extras
+ // so that applications that set their own extras will see the data they set.
+
+ // This is an unfortunate workaround needed due to a gap in the spec. The spec demands
+ // that extras are present in the "extensible" format, which means that each extra field
+ // must be prefixed with a header ID and a length. However, earlier versions of the spec
+ // made no mention of this, nor did any existing API enforce it. This means users could
+ // set "free form" extras without caring very much whether the implementation wanted to
+ // extend or add to them.
+
+ // The start of the extended info header.
+ final int extendedInfoHeaderStart = extendedInfoStart - 4;
+ // The total size of the extended info, including the header.
+ final int extendedInfoTotalSize = extendedInfoSize + 4;
+
+ final int extrasLen = ze.extra.length - extendedInfoTotalSize;
+ byte[] extrasWithoutZip64 = new byte[extrasLen];
+
+ System.arraycopy(ze.extra, 0, extrasWithoutZip64, 0, extendedInfoHeaderStart);
+ System.arraycopy(ze.extra, extendedInfoHeaderStart + extendedInfoTotalSize,
+ extrasWithoutZip64, extendedInfoHeaderStart, (extrasLen - extendedInfoHeaderStart));
+
+ ze.extra = extrasWithoutZip64;
+ return true;
+ }
+ }
+
+ /**
+ * Appends a zip64 extended info record to the extras contained in {@code ze}. If {@code ze}
+ * contains no extras, a new extras array is created.
+ */
+ public static void insertZip64ExtendedInfoToExtras(ZipEntry ze) throws ZipException {
+ final byte[] output;
+ // We add 4 to ZIP64_EXTENDED_INFO_MIN_SIZE to account for the 2 byte header and length.
+ final int extendedInfoSize = ZIP64_EXTENDED_INFO_MIN_SIZE + 4;
+ if (ze.extra == null) {
+ output = new byte[extendedInfoSize];
+ } else {
+ // If the existing extras are already too big, we have no choice but to throw
+ // an error.
+ if (ze.extra.length + extendedInfoSize > 65535) {
+ throw new ZipException("No space in extras for zip64 extended entry info");
+ }
+
+ // We copy existing extras over and put the zip64 extended info at the beginning. This
+ // is to avoid breakages in the presence of "old style" extras which don't contain
+ // headers and lengths. The spec is again silent about these inconsistencies.
+ //
+ // This means that people that for ZipOutputStream users, the value ZipEntry.getExtra
+ // after an entry is written will be different from before. This shouldn't be an issue
+ // in practice.
+ output = new byte[ze.extra.length + ZIP64_EXTENDED_INFO_MIN_SIZE + 4];
+ System.arraycopy(ze.extra, 0, output, ZIP64_EXTENDED_INFO_MIN_SIZE + 4, ze.extra.length);
+ }
+
+ ByteBuffer bb = ByteBuffer.wrap(output).order(ByteOrder.LITTLE_ENDIAN);
+ bb.putShort(ZIP64_EXTENDED_INFO_HEADER_ID);
+ bb.putShort((short) ZIP64_EXTENDED_INFO_MIN_SIZE);
+
+ if (ze.getMethod() == ZipEntry.STORED) {
+ bb.putLong(ze.size);
+ bb.putLong(ze.compressedSize);
+ } else {
+ // Store these fields in the data descriptor instead.
+ bb.putLong(0); // size.
+ bb.putLong(0); // compressed size.
+ }
+
+ // The offset is only relevant in the central directory entry, but we write it out here
+ // anyway, since we know what it is.
+ bb.putLong(ze.localHeaderRelOffset);
+ bb.putInt(0); // disk number
+
+ ze.extra = output;
+ }
+
+ /**
+ * Returns the size of the extended info record if {@code extras} contains a zip64 extended info
+ * record, {@code -1} otherwise. The buffer will be positioned at the start of the extended info
+ * record.
+ */
+ private static int getZip64ExtendedInfoSize(ByteBuffer extras) {
+ try {
+ while (extras.hasRemaining()) {
+ final int headerId = extras.getShort() & 0xffff;
+ final int length = extras.getShort() & 0xffff;
+ if (headerId == ZIP64_EXTENDED_INFO_HEADER_ID) {
+ if (extras.remaining() >= length) {
+ return length;
+ } else {
+ return -1;
+ }
+ } else {
+ extras.position(extras.position() + length);
+ }
+ }
+
+ return -1;
+ } catch (BufferUnderflowException bue) {
+ // We'll underflow if we have an incomplete header in our extras.
+ return -1;
+ } catch (IllegalArgumentException iae) {
+ // ByteBuffer.position() will throw if we have a truncated extra or
+ // an invalid length in the header.
+ return -1;
+ }
+ }
+
+ /**
+ * Copy the size, compressed size and local header offset fields from {@code ze} to
+ * inside {@code ze}'s extended info record. This is additional step is necessary when
+ * we could calculate the correct sizes only after writing out the entry. In this case,
+ * the local file header would not contain real sizes, and they would be present in the
+ * data descriptor and the central directory only.
+ */
+ public static void refreshZip64ExtendedInfo(ZipEntry ze) {
+ if (ze.extra == null || ze.extra.length < ZIP64_EXTENDED_INFO_MIN_SIZE) {
+ throw new IllegalStateException("Zip64 entry has no available extras: " + ze);
+ }
+
+
+ ByteBuffer buf = ByteBuffer.wrap(ze.extra).order(ByteOrder.LITTLE_ENDIAN);
+ if (getZip64ExtendedInfoSize(buf) == -1) {
+ throw new IllegalStateException(
+ "Zip64 entry extras has no zip64 extended info record: " + ze);
+ }
+
+ buf.putLong(ze.size);
+ buf.putLong(ze.compressedSize);
+ buf.putLong(ze.localHeaderRelOffset);
+ buf.putInt(0); // disk number.
+ }
+
+ public static void writeZip64EocdRecordAndLocator(ByteArrayOutputStream baos,
+ long numEntries, long offset, long cDirSize) throws IOException {
+ // Step 1: Write out the zip64 EOCD record.
+ writeLongAsUint32(baos, ZIP64_EOCD_RECORD_SIGNATURE);
+ // The size of the zip64 eocd record. This is the effective size + the
+ // size of the "version made by" (2 bytes) and the "version needed to extract" (2 bytes)
+ // fields.
+ writeLongAsUint64(baos, ZIP64_EOCD_RECORD_EFFECTIVE_SIZE + 4);
+ // TODO: What values should we put here ? The pre-zip64 values we've chosen don't
+ // seem to make much sense either.
+ writeIntAsUint16(baos, 20);
+ writeIntAsUint16(baos, 20);
+ writeLongAsUint32(baos, 0L); // number of disk
+ writeLongAsUint32(baos, 0L); // number of disk with start of central dir.
+ writeLongAsUint64(baos, numEntries); // number of entries in this disk.
+ writeLongAsUint64(baos, numEntries); // number of entries in total.
+ writeLongAsUint64(baos, cDirSize); // size of the central directory.
+ writeLongAsUint64(baos, offset); // offset of the central directory wrt. this file.
+
+ // Step 2: Write out the zip64 EOCD record locator.
+ writeLongAsUint32(baos, ZIP64_LOCATOR_SIGNATURE);
+ writeLongAsUint32(baos, 0); // number of disk with start of central dir.
+ writeLongAsUint64(baos, offset + cDirSize); // offset of the eocd record wrt. this file.
+ writeLongAsUint32(baos, 1); // total number of disks.
+ }
+}
diff --git a/luni/src/main/java/java/util/zip/ZipEntry.java b/luni/src/main/java/java/util/zip/ZipEntry.java
index 217cc3c..26f6863 100644
--- a/luni/src/main/java/java/util/zip/ZipEntry.java
+++ b/luni/src/main/java/java/util/zip/ZipEntry.java
@@ -52,7 +52,6 @@ public class ZipEntry implements ZipConstants, Cloneable {
byte[] extra;
- int nameLength = -1;
long localHeaderRelOffset = -1;
long dataOffset = -1;
@@ -69,7 +68,7 @@ public class ZipEntry implements ZipConstants, Cloneable {
ZipEntry(String name, String comment, long crc, long compressedSize,
long size, int compressionMethod, int time, int modDate, byte[] extra,
- int nameLength, long localHeaderRelOffset, long dataOffset) {
+ long localHeaderRelOffset, long dataOffset) {
this.name = name;
this.comment = comment;
this.crc = crc;
@@ -79,7 +78,6 @@ public class ZipEntry implements ZipConstants, Cloneable {
this.time = time;
this.modDate = modDate;
this.extra = extra;
- this.nameLength = nameLength;
this.localHeaderRelOffset = localHeaderRelOffset;
this.dataOffset = dataOffset;
}
@@ -149,6 +147,11 @@ public class ZipEntry implements ZipConstants, Cloneable {
/**
* Gets the name of this {@code ZipEntry}.
*
+ * <p><em>Security note:</em> Entry names can represent relative paths. {@code foo/../bar} or
+ * {@code ../bar/baz}, for example. If the entry name is being used to construct a filename
+ * or as a path component, it must be validated or sanitized to ensure that files are not
+ * written outside of the intended destination directory.
+ *
* @return the entry name.
*/
public String getName() {
@@ -265,17 +268,15 @@ public class ZipEntry implements ZipConstants, Cloneable {
/**
* Sets the uncompressed size of this {@code ZipEntry}.
*
- * @param value
- * the uncompressed size for this entry.
- * @throws IllegalArgumentException
- * if {@code value} < 0 or {@code value} > 0xFFFFFFFFL.
+ * @param value the uncompressed size for this entry.
+ * @throws IllegalArgumentException if {@code value < 0}.
*/
public void setSize(long value) {
- if (value >= 0 && value <= 0xFFFFFFFFL) {
- size = value;
- } else {
+ if (value < 0) {
throw new IllegalArgumentException("Bad size: " + value);
}
+
+ size = value;
}
/**
@@ -340,7 +341,6 @@ public class ZipEntry implements ZipConstants, Cloneable {
compressionMethod = ze.compressionMethod;
modDate = ze.modDate;
extra = ze.extra;
- nameLength = ze.nameLength;
localHeaderRelOffset = ze.localHeaderRelOffset;
dataOffset = ze.dataOffset;
}
@@ -378,7 +378,7 @@ public class ZipEntry implements ZipConstants, Cloneable {
* On exit, "in" will be positioned at the start of the next entry
* in the Central Directory.
*/
- ZipEntry(byte[] cdeHdrBuf, InputStream cdStream, Charset defaultCharset) throws IOException {
+ ZipEntry(byte[] cdeHdrBuf, InputStream cdStream, Charset defaultCharset, boolean isZip64) throws IOException {
Streams.readFully(cdStream, cdeHdrBuf, 0, cdeHdrBuf.length);
BufferIterator it = HeapBufferIterator.iterator(cdeHdrBuf, 0, cdeHdrBuf.length,
@@ -412,7 +412,7 @@ public class ZipEntry implements ZipConstants, Cloneable {
compressedSize = ((long) it.readInt()) & 0xffffffffL;
size = ((long) it.readInt()) & 0xffffffffL;
- nameLength = it.readShort() & 0xffff;
+ int nameLength = it.readShort() & 0xffff;
int extraLength = it.readShort() & 0xffff;
int commentByteCount = it.readShort() & 0xffff;
@@ -437,6 +437,10 @@ public class ZipEntry implements ZipConstants, Cloneable {
Streams.readFully(cdStream, commentBytes, 0, commentByteCount);
comment = new String(commentBytes, 0, commentBytes.length, charset);
}
+
+ if (isZip64) {
+ Zip64.parseZip64ExtendedInfo(this, true /* from central directory */);
+ }
}
private static boolean containsNulByte(byte[] bytes) {
diff --git a/luni/src/main/java/java/util/zip/ZipFile.java b/luni/src/main/java/java/util/zip/ZipFile.java
index b44156e..307e7fe 100644
--- a/luni/src/main/java/java/util/zip/ZipFile.java
+++ b/luni/src/main/java/java/util/zip/ZipFile.java
@@ -107,6 +107,18 @@ public class ZipFile implements Closeable, ZipConstants {
private final CloseGuard guard = CloseGuard.get();
+ static class EocdRecord {
+ final long numEntries;
+ final long centralDirOffset;
+ final int commentLength;
+
+ EocdRecord(long numEntries, long centralDirOffset, int commentLength) {
+ this.numEntries = numEntries;
+ this.centralDirOffset = centralDirOffset;
+ this.commentLength = commentLength;
+ }
+ }
+
/**
* Constructs a new {@code ZipFile} allowing read access to the contents of the given file.
*
@@ -390,9 +402,11 @@ public class ZipFile implements Closeable, ZipConstants {
stopOffset = 0;
}
+ long eocdOffset;
while (true) {
raf.seek(scanOffset);
if (Integer.reverseBytes(raf.readInt()) == ENDSIG) {
+ eocdOffset = scanOffset;
break;
}
@@ -402,41 +416,35 @@ public class ZipFile implements Closeable, ZipConstants {
}
}
- // Read the End Of Central Directory. ENDHDR includes the signature bytes,
- // which we've already read.
- byte[] eocd = new byte[ENDHDR - 4];
- raf.readFully(eocd);
+ final long zip64EocdRecordOffset = Zip64.parseZip64EocdRecordLocator(raf, eocdOffset);
- // Pull out the information we need.
- BufferIterator it = HeapBufferIterator.iterator(eocd, 0, eocd.length, ByteOrder.LITTLE_ENDIAN);
- int diskNumber = it.readShort() & 0xffff;
- int diskWithCentralDir = it.readShort() & 0xffff;
- int numEntries = it.readShort() & 0xffff;
- int totalNumEntries = it.readShort() & 0xffff;
- it.skip(4); // Ignore centralDirSize.
- long centralDirOffset = ((long) it.readInt()) & 0xffffffffL;
- int commentLength = it.readShort() & 0xffff;
-
- if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) {
- throw new ZipException("Spanned archives not supported");
- }
-
- if (commentLength > 0) {
- byte[] commentBytes = new byte[commentLength];
+ // Seek back past the eocd signature so that we can continue with our search.
+ // Note that we add 4 bytes to the offset to skip past the signature.
+ EocdRecord record = parseEocdRecord(raf, eocdOffset + 4, (zip64EocdRecordOffset != -1) /* isZip64 */);
+ // Read the comment now to avoid an additional seek. We also know the commentLength
+ // won't change because that information isn't present in the zip64 eocd record.
+ if (record.commentLength > 0) {
+ byte[] commentBytes = new byte[record.commentLength];
raf.readFully(commentBytes);
comment = new String(commentBytes, 0, commentBytes.length, StandardCharsets.UTF_8);
}
+ // We have a zip64 eocd record : use that for getting the information we need.
+ if (zip64EocdRecordOffset != -1) {
+ record = Zip64.parseZip64EocdRecord(raf, zip64EocdRecordOffset, record.commentLength);
+ }
+
// Seek to the first CDE and read all entries.
// We have to do this now (from the constructor) rather than lazily because the
// public API doesn't allow us to throw IOException except from the constructor
// or from getInputStream.
- RAFStream rafStream = new RAFStream(raf, centralDirOffset);
+ RAFStream rafStream = new RAFStream(raf, record.centralDirOffset);
BufferedInputStream bufferedStream = new BufferedInputStream(rafStream, 4096);
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, StandardCharsets.UTF_8);
- if (newEntry.localHeaderRelOffset >= centralDirOffset) {
+ for (long i = 0; i < record.numEntries; ++i) {
+ ZipEntry newEntry = new ZipEntry(hdrBuf, bufferedStream, StandardCharsets.UTF_8,
+ (zip64EocdRecordOffset != -1) /* isZip64 */);
+ if (newEntry.localHeaderRelOffset >= record.centralDirOffset) {
throw new ZipException("Local file header offset is after central directory");
}
String entryName = newEntry.getName();
@@ -446,6 +454,45 @@ public class ZipFile implements Closeable, ZipConstants {
}
}
+ private static EocdRecord parseEocdRecord(RandomAccessFile raf, long offset, boolean isZip64) throws IOException {
+ raf.seek(offset);
+
+ // Read the End Of Central Directory. ENDHDR includes the signature bytes,
+ // which we've already read.
+ byte[] eocd = new byte[ENDHDR - 4];
+ raf.readFully(eocd);
+
+ BufferIterator it = HeapBufferIterator.iterator(eocd, 0, eocd.length, ByteOrder.LITTLE_ENDIAN);
+ final long numEntries;
+ final long centralDirOffset;
+ if (isZip64) {
+ numEntries = -1;
+ centralDirOffset = -1;
+
+ // If we have a zip64 end of central directory record, we skip through the regular
+ // end of central directory record and use the information from the zip64 eocd record.
+ // We're still forced to read the comment length (below) since it isn't present in the
+ // zip64 eocd record.
+ it.skip(16);
+ } else {
+ // If we don't have a zip64 eocd record, we read values from the "regular"
+ // eocd record.
+ int diskNumber = it.readShort() & 0xffff;
+ int diskWithCentralDir = it.readShort() & 0xffff;
+ numEntries = it.readShort() & 0xffff;
+ int totalNumEntries = it.readShort() & 0xffff;
+ it.skip(4); // Ignore centralDirSize.
+
+ centralDirOffset = ((long) it.readInt()) & 0xffffffffL;
+ if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) {
+ throw new ZipException("Spanned archives not supported");
+ }
+ }
+
+ final int commentLength = it.readShort() & 0xffff;
+ return new EocdRecord(numEntries, centralDirOffset, commentLength);
+ }
+
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);
diff --git a/luni/src/main/java/java/util/zip/ZipInputStream.java b/luni/src/main/java/java/util/zip/ZipInputStream.java
index 4c0034e..f3ca74e 100644
--- a/luni/src/main/java/java/util/zip/ZipInputStream.java
+++ b/luni/src/main/java/java/util/zip/ZipInputStream.java
@@ -81,7 +81,9 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants
private ZipEntry currentEntry;
- private final byte[] hdrBuf = new byte[LOCHDR - LOCVER];
+ private boolean currentEntryIsZip64;
+
+ private final byte[] hdrBuf = new byte[LOCHDR - LOCVER + 8];
private final CRC32 crc = new CRC32();
@@ -159,7 +161,7 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants
}
try {
- readAndVerifyDataDescriptor(inB, out);
+ readAndVerifyDataDescriptor(inB, out, currentEntryIsZip64);
} catch (Exception e) {
if (failure == null) { // otherwise we're already going to throw
failure = e;
@@ -183,16 +185,31 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants
}
}
- private void readAndVerifyDataDescriptor(int inB, int out) throws IOException {
+ private void readAndVerifyDataDescriptor(long inB, long out, boolean isZip64) throws IOException {
if (hasDD) {
- Streams.readFully(in, hdrBuf, 0, EXTHDR);
+ if (isZip64) {
+ // 8 additional bytes since the compressed / uncompressed size fields
+ // in the extended header are 8 bytes each, instead of 4 bytes each.
+ Streams.readFully(in, hdrBuf, 0, EXTHDR + 8);
+ } else {
+ Streams.readFully(in, hdrBuf, 0, EXTHDR);
+ }
+
int sig = Memory.peekInt(hdrBuf, 0, ByteOrder.LITTLE_ENDIAN);
if (sig != (int) EXTSIG) {
throw new ZipException(String.format("unknown format (EXTSIG=%x)", sig));
}
currentEntry.crc = ((long) Memory.peekInt(hdrBuf, EXTCRC, ByteOrder.LITTLE_ENDIAN)) & 0xffffffffL;
- currentEntry.compressedSize = ((long) Memory.peekInt(hdrBuf, EXTSIZ, ByteOrder.LITTLE_ENDIAN)) & 0xffffffffL;
- currentEntry.size = ((long) Memory.peekInt(hdrBuf, EXTLEN, ByteOrder.LITTLE_ENDIAN)) & 0xffffffffL;
+
+ if (isZip64) {
+ currentEntry.compressedSize = Memory.peekLong(hdrBuf, EXTSIZ, ByteOrder.LITTLE_ENDIAN);
+ // Note that we apply an adjustment of 4 bytes to the offset of EXTLEN to account
+ // for the 8 byte size for zip64.
+ currentEntry.size = Memory.peekLong(hdrBuf, EXTLEN + 4, ByteOrder.LITTLE_ENDIAN);
+ } else {
+ currentEntry.compressedSize = ((long) Memory.peekInt(hdrBuf, EXTSIZ, ByteOrder.LITTLE_ENDIAN)) & 0xffffffffL;
+ currentEntry.size = ((long) Memory.peekInt(hdrBuf, EXTLEN, ByteOrder.LITTLE_ENDIAN)) & 0xffffffffL;
+ }
}
if (currentEntry.crc != crc.getValue()) {
throw new ZipException("CRC mismatch");
@@ -266,7 +283,11 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants
byte[] extraData = new byte[extraLength];
Streams.readFully(in, extraData, 0, extraLength);
currentEntry.setExtra(extraData);
+ currentEntryIsZip64 = Zip64.parseZip64ExtendedInfo(currentEntry, false /* from central directory */);
+ } else {
+ currentEntryIsZip64 = false;
}
+
return currentEntry;
}
diff --git a/luni/src/main/java/java/util/zip/ZipOutputStream.java b/luni/src/main/java/java/util/zip/ZipOutputStream.java
index 8278355..7748cfd 100644
--- a/luni/src/main/java/java/util/zip/ZipOutputStream.java
+++ b/luni/src/main/java/java/util/zip/ZipOutputStream.java
@@ -23,6 +23,8 @@ import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashSet;
+
+import libcore.util.CountingOutputStream;
import libcore.util.EmptyArray;
/**
@@ -85,7 +87,7 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
private final CRC32 crc = new CRC32();
- private int offset = 0, curOffset = 0;
+ private long offset = 0;
/** The charset-encoded name for the current entry. */
private byte[] nameBytes;
@@ -93,6 +95,31 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
/** The charset-encoded comment for the current entry. */
private byte[] entryCommentBytes;
+ private static final byte[] ZIP64_PLACEHOLDER_BYTES =
+ new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
+
+ /**
+ * Whether this zip file needs a Zip64 EOCD record / zip64 EOCD record locator. This
+ * will be true if we wrote an entry whose size or compressed size was too large for
+ * the standard zip format or if we exceeded the maximum number of entries allowed
+ * in the standard format.
+ */
+ private boolean archiveNeedsZip64EocdRecord;
+
+ /**
+ * Whether the current entry being processed needs a zip64 extended info record. This
+ * will be true if the entry is too large for the standard zip format or if the offset
+ * to the start of the current entry header is greater than 0xFFFFFFFF.
+ */
+ private boolean currentEntryNeedsZip64;
+
+ /**
+ * Whether we force all entries in this archive to have a zip64 extended info record.
+ * This of course implies that the {@code currentEntryNeedsZip64} and
+ * {@code archiveNeedsZip64EocdRecord} are always {@code true}.
+ */
+ private final boolean forceZip64;
+
/**
* Constructs a new {@code ZipOutputStream} that writes a zip file to the given
* {@code OutputStream}.
@@ -100,7 +127,15 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
* <p>UTF-8 will be used to encode the file comment, entry names and comments.
*/
public ZipOutputStream(OutputStream os) {
- super(os, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
+ this(os, false /* forceZip64 */);
+ }
+
+ /**
+ * @hide for testing only.
+ */
+ public ZipOutputStream(OutputStream os, boolean forceZip64) {
+ super(new CountingOutputStream(os), new Deflater(Deflater.DEFAULT_COMPRESSION, true));
+ this.forceZip64 = forceZip64;
}
/**
@@ -146,15 +181,30 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
throw new ZipException("Size mismatch");
}
}
- curOffset = LOCHDR;
+
+ long curOffset = LOCHDR;
// Write the DataDescriptor
if (currentEntry.getMethod() != STORED) {
curOffset += EXTHDR;
- writeLong(out, EXTSIG);
- writeLong(out, currentEntry.crc = crc.getValue());
- writeLong(out, currentEntry.compressedSize = def.getTotalOut());
- writeLong(out, currentEntry.size = def.getTotalIn());
+
+ // Data descriptor signature and CRC are 4 bytes each for both zip and zip64.
+ writeLongAsUint32(out, EXTSIG);
+ writeLongAsUint32(out, currentEntry.crc = crc.getValue());
+
+ currentEntry.compressedSize = def.getBytesWritten();
+ currentEntry.size = def.getBytesRead();
+
+ if (currentEntryNeedsZip64) {
+ // We need an additional 8 bytes to store 8 byte compressed / uncompressed
+ // sizes.
+ curOffset += 8;
+ writeLongAsUint64(out, currentEntry.compressedSize);
+ writeLongAsUint64(out, currentEntry.size);
+ } else {
+ writeLongAsUint32(out, currentEntry.compressedSize);
+ writeLongAsUint32(out, currentEntry.size);
+ }
}
// Update the CentralDirectory
// http://www.pkware.com/documents/casestudies/APPNOTE.TXT
@@ -163,33 +213,54 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
// Some tools insist that the central directory have the UTF-8 flag.
// http://code.google.com/p/android/issues/detail?id=20214
flags |= ZipFile.GPBF_UTF8_FLAG;
- writeLong(cDir, CENSIG);
- writeShort(cDir, ZIP_VERSION_2_0); // Version this file was made by.
- writeShort(cDir, ZIP_VERSION_2_0); // Minimum version needed to extract.
- writeShort(cDir, flags);
- writeShort(cDir, currentEntry.getMethod());
- writeShort(cDir, currentEntry.time);
- writeShort(cDir, currentEntry.modDate);
- writeLong(cDir, crc.getValue());
+ writeLongAsUint32(cDir, CENSIG);
+ writeIntAsUint16(cDir, ZIP_VERSION_2_0); // Version this file was made by.
+ writeIntAsUint16(cDir, ZIP_VERSION_2_0); // Minimum version needed to extract.
+ writeIntAsUint16(cDir, flags);
+ writeIntAsUint16(cDir, currentEntry.getMethod());
+ writeIntAsUint16(cDir, currentEntry.time);
+ writeIntAsUint16(cDir, currentEntry.modDate);
+ writeLongAsUint32(cDir, crc.getValue());
+
if (currentEntry.getMethod() == DEFLATED) {
- curOffset += writeLong(cDir, def.getTotalOut());
- writeLong(cDir, def.getTotalIn());
+ currentEntry.setCompressedSize(def.getBytesWritten());
+ currentEntry.setSize(def.getBytesRead());
+ curOffset += currentEntry.getCompressedSize();
} else {
- curOffset += writeLong(cDir, crc.tbytes);
- writeLong(cDir, crc.tbytes);
+ currentEntry.setCompressedSize(crc.tbytes);
+ currentEntry.setSize(crc.tbytes);
+ curOffset += currentEntry.getSize();
}
- curOffset += writeShort(cDir, nameBytes.length);
+
+ if (currentEntryNeedsZip64) {
+ // Refresh the extended info with the compressed size / size before
+ // writing it to the central directory.
+ Zip64.refreshZip64ExtendedInfo(currentEntry);
+
+ // NOTE: We would've written out the zip64 extended info locator to the entry
+ // extras while constructing the local file header. There's no need to do it again
+ // here. If we do, there will be a size mismatch since we're calculating offsets
+ // based on the *current* size of the extra data and not based on the size
+ // at the point of writing the LFH.
+ writeLongAsUint32(cDir, Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE);
+ writeLongAsUint32(cDir, Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE);
+ } else {
+ writeLongAsUint32(cDir, currentEntry.getCompressedSize());
+ writeLongAsUint32(cDir, currentEntry.getSize());
+ }
+
+ curOffset += writeIntAsUint16(cDir, nameBytes.length);
if (currentEntry.extra != null) {
- curOffset += writeShort(cDir, currentEntry.extra.length);
+ curOffset += writeIntAsUint16(cDir, currentEntry.extra.length);
} else {
- writeShort(cDir, 0);
+ writeIntAsUint16(cDir, 0);
}
- writeShort(cDir, entryCommentBytes.length); // Comment length.
- writeShort(cDir, 0); // Disk Start
- writeShort(cDir, 0); // Internal File Attributes
- writeLong(cDir, 0); // External File Attributes
- writeLong(cDir, offset);
+ writeIntAsUint16(cDir, entryCommentBytes.length); // Comment length.
+ writeIntAsUint16(cDir, 0); // Disk Start
+ writeIntAsUint16(cDir, 0); // Internal File Attributes
+ writeLongAsUint32(cDir, 0); // External File Attributes
+ writeLongAsUint32(cDir, offset);
cDir.write(nameBytes);
nameBytes = null;
if (currentEntry.extra != null) {
@@ -228,16 +299,32 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
if (currentEntry != null) {
closeEntry();
}
- int cdirSize = cDir.size();
+
+ int cdirEntriesSize = cDir.size();
+ if (archiveNeedsZip64EocdRecord) {
+ Zip64.writeZip64EocdRecordAndLocator(cDir, entries.size(), offset, cdirEntriesSize);
+ }
+
// Write Central Dir End
- writeLong(cDir, ENDSIG);
- writeShort(cDir, 0); // Disk Number
- writeShort(cDir, 0); // Start Disk
- writeShort(cDir, entries.size()); // Number of entries
- writeShort(cDir, entries.size()); // Number of entries
- writeLong(cDir, cdirSize); // Size of central dir
- writeLong(cDir, offset); // Offset of central dir
- writeShort(cDir, commentBytes.length);
+ writeLongAsUint32(cDir, ENDSIG);
+ writeIntAsUint16(cDir, 0); // Disk Number
+ writeIntAsUint16(cDir, 0); // Start Disk
+
+ // Instead of trying to figure out *why* this archive needed a zip64 eocd record,
+ // just delegate all these values to the zip64 eocd record.
+ if (archiveNeedsZip64EocdRecord) {
+ writeIntAsUint16(cDir, 0xFFFF); // Number of entries
+ writeIntAsUint16(cDir, 0xFFFF); // Number of entries
+ writeLongAsUint32(cDir, 0xFFFFFFFF); // Size of central dir
+ writeLongAsUint32(cDir, 0xFFFFFFFF); // Offset of central dir;
+ } else {
+ writeIntAsUint16(cDir, entries.size()); // Number of entries
+ writeIntAsUint16(cDir, entries.size()); // Number of entries
+ writeLongAsUint32(cDir, cdirEntriesSize); // Size of central dir
+ writeLongAsUint32(cDir, offset); // Offset of central dir
+ }
+
+ writeIntAsUint16(cDir, commentBytes.length);
if (commentBytes.length > 0) {
cDir.write(commentBytes);
}
@@ -288,14 +375,8 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
}
checkOpen();
+ checkAndSetZip64Requirements(ze);
- if (entries.contains(ze.name)) {
- throw new ZipException("Entry already exists: " + ze.name);
- }
- if (entries.size() == 64*1024-1) {
- // TODO: support Zip64.
- throw new ZipException("Too many entries for the zip file format's 16-bit entry count");
- }
nameBytes = ze.name.getBytes(StandardCharsets.UTF_8);
checkSizeIsWithinShort("Name", nameBytes);
entryCommentBytes = EmptyArray.BYTE;
@@ -310,6 +391,7 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
ze.setMethod(method);
currentEntry = ze;
+ currentEntry.localHeaderRelOffset = offset;
entries.add(currentEntry.name);
// Local file header.
@@ -318,30 +400,48 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
// Java always outputs UTF-8 filenames. (Before Java 7, the RI didn't set this flag and used
// modified UTF-8. From Java 7, when using UTF_8 it sets this flag and uses normal UTF-8.)
flags |= ZipFile.GPBF_UTF8_FLAG;
- writeLong(out, LOCSIG); // Entry header
- writeShort(out, ZIP_VERSION_2_0); // Minimum version needed to extract.
- writeShort(out, flags);
- writeShort(out, method);
+ writeLongAsUint32(out, LOCSIG); // Entry header
+ writeIntAsUint16(out, ZIP_VERSION_2_0); // Minimum version needed to extract.
+ writeIntAsUint16(out, flags);
+ writeIntAsUint16(out, method);
if (currentEntry.getTime() == -1) {
currentEntry.setTime(System.currentTimeMillis());
}
- writeShort(out, currentEntry.time);
- writeShort(out, currentEntry.modDate);
+ writeIntAsUint16(out, currentEntry.time);
+ writeIntAsUint16(out, currentEntry.modDate);
if (method == STORED) {
- writeLong(out, currentEntry.crc);
- writeLong(out, currentEntry.size);
- writeLong(out, currentEntry.size);
+ writeLongAsUint32(out, currentEntry.crc);
+
+ if (currentEntryNeedsZip64) {
+ // NOTE: According to the spec, we're allowed to use these fields under zip64
+ // as long as the sizes are <= 4G (and omit writing the zip64 extended information header).
+ //
+ // For simplicity, we write the zip64 extended info here even if we only need it
+ // in the central directory (i.e, the case where we're turning on zip64 because the
+ // offset to this entries LFH is > 0xFFFFFFFF).
+ out.write(ZIP64_PLACEHOLDER_BYTES); // compressed size
+ out.write(ZIP64_PLACEHOLDER_BYTES); // uncompressed size
+ } else {
+ writeLongAsUint32(out, currentEntry.size);
+ writeLongAsUint32(out, currentEntry.size);
+ }
} else {
- writeLong(out, 0);
- writeLong(out, 0);
- writeLong(out, 0);
+ writeLongAsUint32(out, 0);
+ writeLongAsUint32(out, 0);
+ writeLongAsUint32(out, 0);
+ }
+
+ writeIntAsUint16(out, nameBytes.length);
+
+ if (currentEntryNeedsZip64) {
+ Zip64.insertZip64ExtendedInfoToExtras(currentEntry);
}
- writeShort(out, nameBytes.length);
+
if (currentEntry.extra != null) {
- writeShort(out, currentEntry.extra.length);
+ writeIntAsUint16(out, currentEntry.extra.length);
} else {
- writeShort(out, 0);
+ writeIntAsUint16(out, 0);
}
out.write(nameBytes);
if (currentEntry.extra != null) {
@@ -349,6 +449,37 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
}
}
+ private void checkAndSetZip64Requirements(ZipEntry entry) {
+ final long totalBytesWritten = getBytesWritten();
+ final long entriesWritten = entries.size();
+
+ currentEntryNeedsZip64 = false;
+ if (forceZip64) {
+ currentEntryNeedsZip64 = true;
+ archiveNeedsZip64EocdRecord = true;
+ return;
+ }
+
+ // In this particular case, we'll write a zip64 eocd record locator and a zip64 eocd
+ // record but we won't actually need zip64 extended info records for any of the individual
+ // entries (unless they trigger the checks below).
+ if (entriesWritten == 64*1024 - 1) {
+ archiveNeedsZip64EocdRecord = true;
+ }
+
+ // Check whether we'll need to write out a zip64 extended info record in both the local file header
+ // and the central directory. In addition, we will need a zip64 eocd record locator
+ // and record to mark this archive as zip64.
+ //
+ // TODO: This is an imprecise check. When method != STORED it's possible that the compressed
+ // size will be (slightly) larger than the actual size. How can we improve this ?
+ if (totalBytesWritten > Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE ||
+ (entry.getSize() > Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE)) {
+ currentEntryNeedsZip64 = true;
+ archiveNeedsZip64EocdRecord = true;
+ }
+ }
+
/**
* Sets the comment associated with the file being written. See {@link ZipFile#getComment}.
* @throws IllegalArgumentException if the comment is >= 64 Ki encoded bytes.
@@ -386,7 +517,7 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
defaultCompressionMethod = method;
}
- private long writeLong(OutputStream os, long i) throws IOException {
+ static long writeLongAsUint32(OutputStream os, long i) throws IOException {
// Write out the long value as an unsigned int
os.write((int) (i & 0xFF));
os.write((int) (i >> 8) & 0xFF);
@@ -395,7 +526,23 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
return i;
}
- private int writeShort(OutputStream os, int i) throws IOException {
+ static long writeLongAsUint64(OutputStream os, long i) throws IOException {
+ int i1 = (int) i;
+ os.write(i1 & 0xFF);
+ os.write((i1 >> 8) & 0xFF);
+ os.write((i1 >> 16) & 0xFF);
+ os.write((i1 >> 24) & 0xFF);
+
+ int i2 = (int) (i >> 32);
+ os.write(i2 & 0xFF);
+ os.write((i2 >> 8) & 0xFF);
+ os.write((i2 >> 16) & 0xFF);
+ os.write((i2 >> 24) & 0xFF);
+
+ return i;
+ }
+
+ static int writeIntAsUint16(OutputStream os, int i) throws IOException {
os.write(i & 0xFF);
os.write((i >> 8) & 0xFF);
return i;
@@ -414,6 +561,13 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
throw new ZipException("No active entry");
}
+ final long totalBytes = crc.tbytes + byteCount;
+ if ((totalBytes > Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) && !currentEntryNeedsZip64) {
+ throw new IOException("Zip entry size (" + totalBytes +
+ " bytes) cannot be represented in the zip format (needs Zip64)." +
+ " Set the entry length using ZipEntry#setLength to use Zip64 where necessary.");
+ }
+
if (currentEntry.getMethod() == STORED) {
out.write(buffer, offset, byteCount);
} else {
@@ -434,4 +588,11 @@ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstant
" bytes");
}
}
+
+ private long getBytesWritten() {
+ // This cast is somewhat messy but less error prone than keeping an
+ // CountingOutputStream reference around in addition to the FilterOutputStream's
+ // out.
+ return ((CountingOutputStream) out).getCount();
+ }
}
diff --git a/luni/src/main/java/javax/crypto/Cipher.java b/luni/src/main/java/javax/crypto/Cipher.java
index 2e3b341..66d03ad 100644
--- a/luni/src/main/java/javax/crypto/Cipher.java
+++ b/luni/src/main/java/javax/crypto/Cipher.java
@@ -366,8 +366,10 @@ public class Cipher {
/**
* Convenience call when the Key is not available.
+ *
+ * @hide
*/
- private CipherSpi getSpi() {
+ public CipherSpi getSpi() {
return getSpi(null);
}
@@ -419,7 +421,7 @@ public class Cipher {
if (service == null) {
return null;
}
- return tryTransformWithProvider(key, transformParts, type, service);
+ return tryTransformWithProvider(null, transformParts, type, service);
}
ArrayList<Provider.Service> services = ENGINE.getServices(transform);
if (services == null) {
@@ -1005,8 +1007,7 @@ public class Cipher {
* the offset in the input to start.
* @param inputLen
* the length of the input to transform.
- * @return the transformed bytes in a new buffer, or {@code null} if the
- * input has zero length.
+ * @return the transformed bytes in a new buffer, or {@code null} if {@code inputLen} is zero.
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
@@ -1023,7 +1024,7 @@ public class Cipher {
throw new IllegalArgumentException("input == null");
}
checkInputOffsetAndCount(input.length, inputOffset, inputLen);
- if (input.length == 0) {
+ if (inputLen == 0) {
return null;
}
return getSpi().engineUpdate(input, inputOffset, inputLen);
diff --git a/luni/src/main/java/javax/crypto/ExemptionMechanism.java b/luni/src/main/java/javax/crypto/ExemptionMechanism.java
index c2d42e6..eede649 100644
--- a/luni/src/main/java/javax/crypto/ExemptionMechanism.java
+++ b/luni/src/main/java/javax/crypto/ExemptionMechanism.java
@@ -361,9 +361,6 @@ public class ExemptionMechanism {
return len;
}
- /**
- * Override to clear any key state in the instance.
- */
@Override protected void finalize() {
try {
super.finalize();
diff --git a/luni/src/main/java/javax/crypto/KeyAgreement.java b/luni/src/main/java/javax/crypto/KeyAgreement.java
index abcfd0e..d27aa2e 100644
--- a/luni/src/main/java/javax/crypto/KeyAgreement.java
+++ b/luni/src/main/java/javax/crypto/KeyAgreement.java
@@ -195,7 +195,7 @@ public class KeyAgreement {
if (service == null) {
return null;
}
- return tryAlgorithmWithProvider(key, service);
+ return tryAlgorithmWithProvider(null, service);
}
ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
if (services == null) {
@@ -252,8 +252,10 @@ public class KeyAgreement {
/**
* Convenience call when the Key is not available.
+ *
+ * @hide
*/
- private KeyAgreementSpi getSpi() {
+ public KeyAgreementSpi getSpi() {
return getSpi(null);
}
diff --git a/luni/src/main/java/javax/crypto/Mac.java b/luni/src/main/java/javax/crypto/Mac.java
index 5a73dc5..536f0c5 100644
--- a/luni/src/main/java/javax/crypto/Mac.java
+++ b/luni/src/main/java/javax/crypto/Mac.java
@@ -199,7 +199,7 @@ public class Mac implements Cloneable {
if (service == null) {
return null;
}
- return tryAlgorithmWithProvider(key, service);
+ return tryAlgorithmWithProvider(null, service);
}
ArrayList<Provider.Service> services = ENGINE.getServices(algorithm);
if (services == null) {
@@ -266,8 +266,10 @@ public class Mac implements Cloneable {
/**
* Convenience call when the Key is not available.
+ *
+ * @hide
*/
- private MacSpi getSpi() {
+ public MacSpi getSpi() {
return getSpi(null);
}
diff --git a/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java b/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
deleted file mode 100644
index fd84c3e..0000000
--- a/luni/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
+++ /dev/null
@@ -1,169 +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 javax.net.ssl;
-
-import java.net.InetAddress;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import javax.security.auth.x500.X500Principal;
-
-/**
- * A HostnameVerifier consistent with <a
- * href="http://www.ietf.org/rfc/rfc2818.txt">RFC 2818</a>.
- *
- * @hide accessible via HttpsURLConnection.getDefaultHostnameVerifier()
- */
-public final class DefaultHostnameVerifier implements HostnameVerifier {
- private static final int ALT_DNS_NAME = 2;
- private static final int ALT_IPA_NAME = 7;
-
- public final boolean verify(String host, SSLSession session) {
- try {
- Certificate[] certificates = session.getPeerCertificates();
- return verify(host, (X509Certificate) certificates[0]);
- } catch (SSLException e) {
- return false;
- }
- }
-
- public boolean verify(String host, X509Certificate certificate) {
- return InetAddress.isNumeric(host)
- ? verifyIpAddress(host, certificate)
- : verifyHostName(host, certificate);
- }
-
- /**
- * Returns true if {@code certificate} matches {@code ipAddress}.
- */
- private boolean verifyIpAddress(String ipAddress, X509Certificate certificate) {
- for (String altName : getSubjectAltNames(certificate, ALT_IPA_NAME)) {
- if (ipAddress.equalsIgnoreCase(altName)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns true if {@code certificate} matches {@code hostName}.
- */
- private boolean verifyHostName(String hostName, X509Certificate certificate) {
- hostName = hostName.toLowerCase(Locale.US);
- boolean hasDns = false;
- for (String altName : getSubjectAltNames(certificate, ALT_DNS_NAME)) {
- hasDns = true;
- if (verifyHostName(hostName, altName)) {
- return true;
- }
- }
-
- if (!hasDns) {
- X500Principal principal = certificate.getSubjectX500Principal();
- // RFC 2818 advises using the most specific name for matching.
- String cn = new DistinguishedNameParser(principal).findMostSpecific("cn");
- if (cn != null) {
- return verifyHostName(hostName, cn);
- }
- }
-
- return false;
- }
-
- private List<String> getSubjectAltNames(X509Certificate certificate, int type) {
- List<String> result = new ArrayList<String>();
- try {
- Collection<?> subjectAltNames = certificate.getSubjectAlternativeNames();
- if (subjectAltNames == null) {
- return Collections.emptyList();
- }
- for (Object subjectAltName : subjectAltNames) {
- List<?> entry = (List<?>) subjectAltName;
- if (entry == null || entry.size() < 2) {
- continue;
- }
- Integer altNameType = (Integer) entry.get(0);
- if (altNameType == null) {
- continue;
- }
- if (altNameType == type) {
- String altName = (String) entry.get(1);
- if (altName != null) {
- result.add(altName);
- }
- }
- }
- return result;
- } catch (CertificateParsingException e) {
- return Collections.emptyList();
- }
- }
-
- /**
- * Returns true if {@code hostName} matches the name or pattern {@code cn}.
- *
- * @param hostName lowercase host name.
- * @param cn certificate host name. May include wildcards like
- * {@code *.android.com}.
- */
- public boolean verifyHostName(String hostName, String cn) {
- if (hostName == null || hostName.isEmpty() || cn == null || cn.isEmpty()) {
- return false;
- }
-
- cn = cn.toLowerCase(Locale.US);
-
- if (!cn.contains("*")) {
- return hostName.equals(cn);
- }
-
- if (cn.startsWith("*.") && hostName.equals(cn.substring(2))) {
- return true; // "*.foo.com" matches "foo.com"
- }
-
- int asterisk = cn.indexOf('*');
- int dot = cn.indexOf('.');
- if (asterisk > dot) {
- return false; // malformed; wildcard must be in the first part of the cn
- }
-
- if (!hostName.regionMatches(0, cn, 0, asterisk)) {
- return false; // prefix before '*' doesn't match
- }
-
- 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 '.'
- }
- }
-
- if (!hostName.regionMatches(suffixStart, cn, asterisk + 1, suffixLength)) {
- return false; // suffix after '*' doesn't match
- }
-
- return true;
- }
-}
diff --git a/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java b/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java
index ab86a9b..1bd48fd 100644
--- a/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java
+++ b/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java
@@ -109,7 +109,16 @@ public abstract class HttpsURLConnection extends HttpURLConnection {
* it.
*/
private static class NoPreloadHolder {
- public static HostnameVerifier defaultHostnameVerifier = new DefaultHostnameVerifier();
+ public static HostnameVerifier defaultHostnameVerifier;
+ static {
+ try {
+ defaultHostnameVerifier = (HostnameVerifier)
+ Class.forName("com.android.okhttp.internal.tls.OkHostnameVerifier")
+ .getField("INSTANCE").get(null);
+ } catch (Exception e) {
+ throw new AssertionError("Failed to obtain okhttp HostnameVerifier", e);
+ }
+ }
public static SSLSocketFactory defaultSSLSocketFactory = (SSLSocketFactory) SSLSocketFactory
.getDefault();
diff --git a/luni/src/main/java/javax/net/ssl/SSLEngine.java b/luni/src/main/java/javax/net/ssl/SSLEngine.java
index cbf02ac..f40f4b0 100644
--- a/luni/src/main/java/javax/net/ssl/SSLEngine.java
+++ b/luni/src/main/java/javax/net/ssl/SSLEngine.java
@@ -73,17 +73,17 @@ import java.nio.ByteBuffer;
* <tbody>
* <tr>
* <td>SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA</td>
- * <td>9+</td>
+ * <td>9-22</td>
* <td>9-19</td>
* </tr>
* <tr>
* <td>SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA</td>
- * <td>9+</td>
+ * <td>9-22</td>
* <td>9-19</td>
* </tr>
* <tr>
* <td>SSL_DHE_DSS_WITH_DES_CBC_SHA</td>
- * <td>9+</td>
+ * <td>9-22</td>
* <td>9-19</td>
* </tr>
* <tr>
@@ -178,32 +178,32 @@ import java.nio.ByteBuffer;
* </tr>
* <tr>
* <td>TLS_DHE_DSS_WITH_AES_128_CBC_SHA</td>
- * <td>9+</td>
- * <td>9+</td>
+ * <td>9-22</td>
+ * <td>9-22</td>
* </tr>
* <tr>
* <td>TLS_DHE_DSS_WITH_AES_128_CBC_SHA256</td>
- * <td>20+</td>
+ * <td>20-22</td>
* <td></td>
* </tr>
* <tr>
* <td>TLS_DHE_DSS_WITH_AES_128_GCM_SHA256</td>
- * <td>20+</td>
+ * <td>20-22</td>
* <td></td>
* </tr>
* <tr>
* <td>TLS_DHE_DSS_WITH_AES_256_CBC_SHA</td>
- * <td>9+</td>
- * <td>20+</td>
+ * <td>9-22</td>
+ * <td>20-22</td>
* </tr>
* <tr>
* <td>TLS_DHE_DSS_WITH_AES_256_CBC_SHA256</td>
- * <td>20+</td>
+ * <td>20-22</td>
* <td></td>
* </tr>
* <tr>
* <td>TLS_DHE_DSS_WITH_AES_256_GCM_SHA384</td>
- * <td>20+</td>
+ * <td>20-22</td>
* <td></td>
* </tr>
* <tr>
diff --git a/luni/src/main/java/javax/net/ssl/SSLSocket.java b/luni/src/main/java/javax/net/ssl/SSLSocket.java
index dc406e1..c6906c5 100644
--- a/luni/src/main/java/javax/net/ssl/SSLSocket.java
+++ b/luni/src/main/java/javax/net/ssl/SSLSocket.java
@@ -117,17 +117,17 @@ import java.net.UnknownHostException;
* <tbody>
* <tr>
* <td>SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA</td>
- * <td>9+</td>
+ * <td>9-22</td>
* <td>9-19</td>
* </tr>
* <tr>
* <td>SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA</td>
- * <td>9+</td>
+ * <td>9-22</td>
* <td>9-19</td>
* </tr>
* <tr>
* <td>SSL_DHE_DSS_WITH_DES_CBC_SHA</td>
- * <td>9+</td>
+ * <td>9-22</td>
* <td>9-19</td>
* </tr>
* <tr>
@@ -212,32 +212,32 @@ import java.net.UnknownHostException;
* </tr>
* <tr>
* <td>TLS_DHE_DSS_WITH_AES_128_CBC_SHA</td>
- * <td>9+</td>
- * <td>9+</td>
+ * <td>9-22</td>
+ * <td>9-22</td>
* </tr>
* <tr>
* <td>TLS_DHE_DSS_WITH_AES_128_CBC_SHA256</td>
- * <td>20+</td>
+ * <td>20-22</td>
* <td></td>
* </tr>
* <tr>
* <td>TLS_DHE_DSS_WITH_AES_128_GCM_SHA256</td>
- * <td>20+</td>
+ * <td>20-22</td>
* <td></td>
* </tr>
* <tr>
* <td>TLS_DHE_DSS_WITH_AES_256_CBC_SHA</td>
- * <td>9+</td>
- * <td>11+</td>
+ * <td>9-22</td>
+ * <td>11-22</td>
* </tr>
* <tr>
* <td>TLS_DHE_DSS_WITH_AES_256_CBC_SHA256</td>
- * <td>20+</td>
+ * <td>20-22</td>
* <td></td>
* </tr>
* <tr>
* <td>TLS_DHE_DSS_WITH_AES_256_GCM_SHA384</td>
- * <td>20+</td>
+ * <td>20-22</td>
* <td></td>
* </tr>
* <tr>
@@ -639,14 +639,14 @@ import java.net.UnknownHostException;
* <tr>
* <td>DHE-DSS-AES128-SHA</td>
* <td>TLS_DHE_DSS_WITH_AES_128_CBC_SHA</td>
- * <td>1+</td>
- * <td>1+</td>
+ * <td>1-22</td>
+ * <td>1-22</td>
* </tr>
* <tr>
* <td>DHE-DSS-AES256-SHA</td>
* <td>TLS_DHE_DSS_WITH_AES_256_CBC_SHA</td>
- * <td>1+</td>
- * <td>1-8, 11+</td>
+ * <td>1-22</td>
+ * <td>1-8, 11-22</td>
* </tr>
* <tr>
* <td>DHE-RSA-AES128-SHA</td>
@@ -663,13 +663,13 @@ import java.net.UnknownHostException;
* <tr>
* <td>EDH-DSS-DES-CBC-SHA</td>
* <td>SSL_DHE_DSS_WITH_DES_CBC_SHA</td>
- * <td>1+</td>
+ * <td>1-22</td>
* <td>1-19</td>
* </tr>
* <tr>
* <td>EDH-DSS-DES-CBC3-SHA</td>
* <td>SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA</td>
- * <td>1+</td>
+ * <td>1-22</td>
* <td>1-19</td>
* </tr>
* <tr>
@@ -693,7 +693,7 @@ import java.net.UnknownHostException;
* <tr>
* <td>EXP-EDH-DSS-DES-CBC-SHA</td>
* <td>SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA</td>
- * <td>1+</td>
+ * <td>1-22</td>
* <td>1-19</td>
* </tr>
* <tr>
diff --git a/luni/src/main/java/javax/security/cert/X509Certificate.java b/luni/src/main/java/javax/security/cert/X509Certificate.java
index e85a556..5084ae0 100644
--- a/luni/src/main/java/javax/security/cert/X509Certificate.java
+++ b/luni/src/main/java/javax/security/cert/X509Certificate.java
@@ -51,7 +51,7 @@ public abstract class X509Certificate extends Certificate {
String classname = Security.getProperty("cert.provider.x509v1");
Class cl = Class.forName(classname);
constructor = cl.getConstructor(new Class[] {InputStream.class});
- } catch (Throwable e) {
+ } catch (Exception|LinkageError e) {
}
}
@@ -80,7 +80,7 @@ public abstract class X509Certificate extends Certificate {
try {
return (X509Certificate)
constructor.newInstance(new Object[] {inStream});
- } catch (Throwable e) {
+ } catch (ReflectiveOperationException e) {
throw new CertificateException(e.getMessage());
}
}
diff --git a/luni/src/main/java/javax/xml/datatype/FactoryFinder.java b/luni/src/main/java/javax/xml/datatype/FactoryFinder.java
index b65f412..1fbca2f 100644
--- a/luni/src/main/java/javax/xml/datatype/FactoryFinder.java
+++ b/luni/src/main/java/javax/xml/datatype/FactoryFinder.java
@@ -47,11 +47,30 @@ final class FactoryFinder {
/** <p>Debug flag to trace loading process.</p> */
private static boolean debug = false;
- /** <p>Cache properties for performance.</p> */
- private static Properties cacheProps = new Properties();
+ /**
+ * <p>Cache properties for performance. Use a static class to avoid double-checked
+ * locking.</p>
+ */
+ private static class CacheHolder {
- /** <p>First time requires initialization overhead.</p> */
- private static boolean firstTime = true;
+ private static Properties cacheProps = new Properties();
+
+ static {
+ String javah = System.getProperty("java.home");
+ String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties";
+ File f = new File(configFile);
+ if (f.exists()) {
+ if (debug) debugPrintln("Read properties file " + f);
+ try {
+ cacheProps.load(new FileInputStream(f));
+ } catch (Exception ex) {
+ if (debug) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ }
+ }
/** Default columns per line. */
private static final int DEFAULT_LINE_LENGTH = 80;
@@ -177,22 +196,7 @@ final class FactoryFinder {
// try to read from $java.home/lib/jaxp.properties
try {
- String javah = System.getProperty("java.home");
- String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties";
- String factoryClassName = null;
- if (firstTime) {
- synchronized (cacheProps) {
- if (firstTime) {
- File f = new File(configFile);
- firstTime = false;
- if (f.exists()) {
- if (debug) debugPrintln("Read properties file " + f);
- cacheProps.load(new FileInputStream(f));
- }
- }
- }
- }
- factoryClassName = cacheProps.getProperty(factoryId);
+ String factoryClassName = CacheHolder.cacheProps.getProperty(factoryId);
if (debug) debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
if (factoryClassName != null) {
diff --git a/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java b/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java
index 636777c..0060612 100644
--- a/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java
+++ b/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java
@@ -49,14 +49,29 @@ final class SchemaFactoryFinder {
private static boolean debug = false;
/**
- * <p>Cache properties for performance.</p>
+ * <p>Cache properties for performance. Use a static class to avoid double-checked
+ * locking.</p>
*/
- private static Properties cacheProps = new Properties();
+ private static class CacheHolder {
- /**
- * <p>First time requires initialization overhead.</p>
- */
- private static boolean firstTime = true;
+ private static Properties cacheProps = new Properties();
+
+ static {
+ String javah = System.getProperty("java.home");
+ String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties";
+ File f = new File(configFile);
+ if (f.exists()) {
+ if (debug) debugPrintln("Read properties file " + f);
+ try {
+ cacheProps.load(new FileInputStream(f));
+ } catch (Exception ex) {
+ if (debug) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ }
+ }
/**
* Default columns per line.
@@ -184,27 +199,9 @@ final class SchemaFactoryFinder {
}
}
- String javah = System.getProperty("java.home");
- String configFile = javah + File.separator +
- "lib" + File.separator + "jaxp.properties";
-
- String factoryClassName = null ;
-
// try to read from $java.home/lib/jaxp.properties
try {
- if(firstTime){
- synchronized(cacheProps){
- if(firstTime){
- File f=new File( configFile );
- firstTime = false;
- if(f.exists()){
- if (debug) debugPrintln("Read properties file " + f);
- cacheProps.load(new FileInputStream(f));
- }
- }
- }
- }
- factoryClassName = cacheProps.getProperty(propertyName);
+ String factoryClassName = CacheHolder.cacheProps.getProperty(propertyName);
if (debug) debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
if (factoryClassName != null) {
diff --git a/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java b/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java
index 0113e7d..5a7663c 100644
--- a/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java
+++ b/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java
@@ -56,14 +56,29 @@ final class XPathFactoryFinder {
}
/**
- * <p>Cache properties for performance.</p>
+ * <p>Cache properties for performance. Use a static class to avoid double-checked
+ * locking.</p>
*/
- private static Properties cacheProps = new Properties();
+ private static class CacheHolder {
- /**
- * <p>First time requires initialization overhead.</p>
- */
- private static boolean firstTime = true;
+ private static Properties cacheProps = new Properties();
+
+ static {
+ String javah = System.getProperty("java.home");
+ String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties";
+ File f = new File(configFile);
+ if (f.exists()) {
+ if (debug) debugPrintln("Read properties file " + f);
+ try {
+ cacheProps.load(new FileInputStream(f));
+ } catch (Exception ex) {
+ if (debug) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ }
+ }
/**
* <p>Conditional debug printing.</p>
@@ -164,27 +179,9 @@ final class XPathFactoryFinder {
e.printStackTrace();
}
- String javah = System.getProperty("java.home");
- String configFile = javah + File.separator +
- "lib" + File.separator + "jaxp.properties";
-
- String factoryClassName = null ;
-
// try to read from $java.home/lib/jaxp.properties
try {
- if(firstTime){
- synchronized(cacheProps){
- if(firstTime){
- File f=new File( configFile );
- firstTime = false;
- if (f.exists()) {
- if (debug) debugPrintln("Read properties file " + f);
- cacheProps.load(new FileInputStream(f));
- }
- }
- }
- }
- factoryClassName = cacheProps.getProperty(propertyName);
+ String factoryClassName = CacheHolder.cacheProps.getProperty(propertyName);
if (debug) debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
if (factoryClassName != null) {
diff --git a/luni/src/main/java/libcore/icu/DateIntervalFormat.java b/luni/src/main/java/libcore/icu/DateIntervalFormat.java
index 3855654..509d0a0 100644
--- a/luni/src/main/java/libcore/icu/DateIntervalFormat.java
+++ b/luni/src/main/java/libcore/icu/DateIntervalFormat.java
@@ -16,48 +16,27 @@
package libcore.icu;
-import java.util.Calendar;
-import java.util.Locale;
+import com.ibm.icu.util.Calendar;
+import com.ibm.icu.util.ULocale;
+
+import java.text.FieldPosition;
import java.util.TimeZone;
import libcore.util.BasicLruCache;
+import static libcore.icu.DateUtilsBridge.FORMAT_UTC;
+
/**
- * Exposes icu4c's DateIntervalFormat.
+ * Exposes icu4j's DateIntervalFormat.
*/
public final class DateIntervalFormat {
- // These are all public API in DateUtils. There are others, but they're either for use with
- // other methods (like FORMAT_ABBREV_RELATIVE), don't internationalize (like FORMAT_CAP_AMPM),
- // or have never been implemented anyway.
- public static final int FORMAT_SHOW_TIME = 0x00001;
- public static final int FORMAT_SHOW_WEEKDAY = 0x00002;
- public static final int FORMAT_SHOW_YEAR = 0x00004;
- public static final int FORMAT_NO_YEAR = 0x00008;
- public static final int FORMAT_SHOW_DATE = 0x00010;
- public static final int FORMAT_NO_MONTH_DAY = 0x00020;
- public static final int FORMAT_12HOUR = 0x00040;
- public static final int FORMAT_24HOUR = 0x00080;
- public static final int FORMAT_UTC = 0x02000;
- public static final int FORMAT_ABBREV_TIME = 0x04000;
- public static final int FORMAT_ABBREV_WEEKDAY = 0x08000;
- public static final int FORMAT_ABBREV_MONTH = 0x10000;
- public static final int FORMAT_NUMERIC_DATE = 0x20000;
- public static final int FORMAT_ABBREV_ALL = 0x80000;
-
- private static final int DAY_IN_MS = 24 * 60 * 60 * 1000;
- private static final int EPOCH_JULIAN_DAY = 2440588;
-
private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();
- static class FormatterCache extends BasicLruCache<String, Long> {
+ static class FormatterCache extends BasicLruCache<String, com.ibm.icu.text.DateIntervalFormat> {
FormatterCache() {
super(8);
}
-
- protected void entryEvicted(String key, Long value) {
- destroyDateIntervalFormat(value);
- }
- };
+ }
private DateIntervalFormat() {
}
@@ -67,131 +46,58 @@ public final class DateIntervalFormat {
if ((flags & FORMAT_UTC) != 0) {
olsonId = "UTC";
}
+ // We create a java.util.TimeZone here to use libcore's data and libcore's olson ID / pseudo-tz
+ // logic.
TimeZone tz = (olsonId != null) ? TimeZone.getTimeZone(olsonId) : TimeZone.getDefault();
- return formatDateRange(Locale.getDefault(), tz, startMs, endMs, flags);
+ com.ibm.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
+ ULocale icuLocale = ULocale.getDefault();
+ return formatDateRange(icuLocale, icuTimeZone, startMs, endMs, flags);
}
// This is our slightly more sensible internal API. (A truly sane replacement would take a
// skeleton instead of int flags.)
- public static String formatDateRange(Locale locale, TimeZone tz, long startMs, long endMs, int flags) {
- Calendar startCalendar = Calendar.getInstance(tz);
- startCalendar.setTimeInMillis(startMs);
-
+ public static String formatDateRange(ULocale icuLocale, com.ibm.icu.util.TimeZone icuTimeZone,
+ long startMs, long endMs, int flags) {
+ Calendar startCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, startMs);
Calendar endCalendar;
if (startMs == endMs) {
endCalendar = startCalendar;
} else {
- endCalendar = Calendar.getInstance(tz);
- endCalendar.setTimeInMillis(endMs);
+ endCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, endMs);
}
boolean endsAtMidnight = isMidnight(endCalendar);
// If we're not showing the time or the start and end times are on the same day, and the
// end time is midnight, fudge the end date so we don't count the day that's about to start.
- // This is not the behavior of icu4c's DateIntervalFormat, but it's the historical behavior
+ // This is not the behavior of icu4j's DateIntervalFormat, but it's the historical behavior
// of Android's DateUtils.formatDateRange.
if (startMs != endMs && endsAtMidnight &&
- ((flags & FORMAT_SHOW_TIME) == 0 || dayDistance(startCalendar, endCalendar) <= 1)) {
+ ((flags & DateUtilsBridge.FORMAT_SHOW_TIME) == 0
+ || DateUtilsBridge.dayDistance(startCalendar, endCalendar) <= 1)) {
endCalendar.roll(Calendar.DAY_OF_MONTH, false);
- endMs -= DAY_IN_MS;
}
- String skeleton = toSkeleton(startCalendar, endCalendar, flags);
+ String skeleton = DateUtilsBridge.toSkeleton(startCalendar, endCalendar, flags);
synchronized (CACHED_FORMATTERS) {
- return formatDateInterval(getFormatter(skeleton, locale.toString(), tz.getID()), startMs, endMs);
+ com.ibm.icu.text.DateIntervalFormat formatter =
+ getFormatter(skeleton, icuLocale, icuTimeZone);
+ return formatter.format(startCalendar, endCalendar, new StringBuffer(),
+ new FieldPosition(0)).toString();
}
}
- private static long getFormatter(String skeleton, String localeName, String tzName) {
- String key = skeleton + "\t" + localeName + "\t" + tzName;
- Long formatter = CACHED_FORMATTERS.get(key);
+ private static com.ibm.icu.text.DateIntervalFormat getFormatter(String skeleton, ULocale locale,
+ com.ibm.icu.util.TimeZone icuTimeZone) {
+ String key = skeleton + "\t" + locale + "\t" + icuTimeZone;
+ com.ibm.icu.text.DateIntervalFormat 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) {
- if ((flags & FORMAT_ABBREV_ALL) != 0) {
- flags |= FORMAT_ABBREV_MONTH | FORMAT_ABBREV_TIME | FORMAT_ABBREV_WEEKDAY;
- }
-
- String monthPart = "MMMM";
- if ((flags & FORMAT_NUMERIC_DATE) != 0) {
- monthPart = "M";
- } else if ((flags & FORMAT_ABBREV_MONTH) != 0) {
- monthPart = "MMM";
- }
-
- String weekPart = "EEEE";
- if ((flags & FORMAT_ABBREV_WEEKDAY) != 0) {
- weekPart = "EEE";
- }
-
- String timePart = "j"; // "j" means choose 12 or 24 hour based on current locale.
- if ((flags & FORMAT_24HOUR) != 0) {
- timePart = "H";
- } else if ((flags & FORMAT_12HOUR) != 0) {
- timePart = "h";
- }
-
- // If we've not been asked to abbreviate times, or we're using the 24-hour clock (where it
- // never makes sense to leave out the minutes), include minutes. This gets us times like
- // "4 PM" while avoiding times like "16" (for "16:00").
- if ((flags & FORMAT_ABBREV_TIME) == 0 || (flags & FORMAT_24HOUR) != 0) {
- timePart += "m";
- } else {
- // Otherwise, we're abbreviating a 12-hour time, and should only show the minutes
- // if they're not both "00".
- if (!(onTheHour(startCalendar) && onTheHour(endCalendar))) {
- timePart = timePart + "m";
- }
- }
-
- if (fallOnDifferentDates(startCalendar, endCalendar)) {
- flags |= FORMAT_SHOW_DATE;
- }
-
- if (fallInSameMonth(startCalendar, endCalendar) && (flags & FORMAT_NO_MONTH_DAY) != 0) {
- flags &= (~FORMAT_SHOW_WEEKDAY);
- flags &= (~FORMAT_SHOW_TIME);
- }
-
- if ((flags & (FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_SHOW_WEEKDAY)) == 0) {
- flags |= FORMAT_SHOW_DATE;
- }
-
- // If we've been asked to show the date, work out whether we think we should show the year.
- if ((flags & FORMAT_SHOW_DATE) != 0) {
- if ((flags & FORMAT_SHOW_YEAR) != 0) {
- // The caller explicitly wants us to show the year.
- } else if ((flags & FORMAT_NO_YEAR) != 0) {
- // The caller explicitly doesn't want us to show the year, even if we otherwise would.
- } else if (!fallInSameYear(startCalendar, endCalendar) || !isThisYear(startCalendar)) {
- flags |= FORMAT_SHOW_YEAR;
- }
- }
-
- StringBuilder builder = new StringBuilder();
- if ((flags & (FORMAT_SHOW_DATE | FORMAT_NO_MONTH_DAY)) != 0) {
- if ((flags & FORMAT_SHOW_YEAR) != 0) {
- builder.append("y");
- }
- builder.append(monthPart);
- if ((flags & FORMAT_NO_MONTH_DAY) == 0) {
- builder.append("d");
- }
- }
- if ((flags & FORMAT_SHOW_WEEKDAY) != 0) {
- builder.append(weekPart);
- }
- if ((flags & FORMAT_SHOW_TIME) != 0) {
- builder.append(timePart);
- }
- return builder.toString();
+ formatter = com.ibm.icu.text.DateIntervalFormat.getInstance(skeleton, locale);
+ formatter.setTimeZone(icuTimeZone);
+ CACHED_FORMATTERS.put(key, formatter);
+ return formatter;
}
private static boolean isMidnight(Calendar c) {
@@ -201,39 +107,4 @@ public final class DateIntervalFormat {
c.get(Calendar.MILLISECOND) == 0;
}
- private static boolean onTheHour(Calendar c) {
- return c.get(Calendar.MINUTE) == 0 && c.get(Calendar.SECOND) == 0;
- }
-
- private static boolean fallOnDifferentDates(Calendar c1, Calendar c2) {
- return c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR) ||
- c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH) ||
- c1.get(Calendar.DAY_OF_MONTH) != c2.get(Calendar.DAY_OF_MONTH);
- }
-
- private static boolean fallInSameMonth(Calendar c1, Calendar c2) {
- return c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH);
- }
-
- private static boolean fallInSameYear(Calendar c1, Calendar c2) {
- return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR);
- }
-
- private static boolean isThisYear(Calendar c) {
- Calendar now = Calendar.getInstance(c.getTimeZone());
- return c.get(Calendar.YEAR) == now.get(Calendar.YEAR);
- }
-
- private static int dayDistance(Calendar c1, Calendar c2) {
- return julianDay(c2) - julianDay(c1);
- }
-
- private static int julianDay(Calendar c) {
- long utcMs = c.getTimeInMillis() + c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET);
- return (int) (utcMs / DAY_IN_MS) + EPOCH_JULIAN_DAY;
- }
-
- 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/java/libcore/icu/DateTimeFormat.java b/luni/src/main/java/libcore/icu/DateTimeFormat.java
new file mode 100644
index 0000000..7323c26
--- /dev/null
+++ b/luni/src/main/java/libcore/icu/DateTimeFormat.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.icu;
+
+import com.ibm.icu.text.DateFormat;
+import com.ibm.icu.text.DateTimePatternGenerator;
+import com.ibm.icu.text.DisplayContext;
+import com.ibm.icu.text.SimpleDateFormat;
+import com.ibm.icu.util.Calendar;
+import com.ibm.icu.util.ULocale;
+
+import libcore.util.BasicLruCache;
+
+/**
+ * A formatter that outputs a single date/time.
+ */
+public class DateTimeFormat {
+ private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();
+
+ static class FormatterCache extends BasicLruCache<String, DateFormat> {
+ FormatterCache() {
+ super(8);
+ }
+ }
+
+ private DateTimeFormat() {
+ }
+
+ public static String format(ULocale icuLocale, Calendar time, int flags,
+ DisplayContext displayContext) {
+ String skeleton = DateUtilsBridge.toSkeleton(time, flags);
+ String key = skeleton + "\t" + icuLocale + "\t" + time.getTimeZone();
+ synchronized(CACHED_FORMATTERS) {
+ DateFormat formatter = CACHED_FORMATTERS.get(key);
+ if (formatter == null) {
+ DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance(icuLocale);
+ formatter = new SimpleDateFormat(generator.getBestPattern(skeleton), icuLocale);
+ CACHED_FORMATTERS.put(key, formatter);
+ }
+ formatter.setContext(displayContext);
+ return formatter.format(time);
+ }
+ }
+}
diff --git a/luni/src/main/java/libcore/icu/DateUtilsBridge.java b/luni/src/main/java/libcore/icu/DateUtilsBridge.java
new file mode 100644
index 0000000..88faa90
--- /dev/null
+++ b/luni/src/main/java/libcore/icu/DateUtilsBridge.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.icu;
+
+import com.ibm.icu.impl.JavaTimeZone;
+import com.ibm.icu.util.Calendar;
+import com.ibm.icu.util.GregorianCalendar;
+import com.ibm.icu.util.ULocale;
+
+/**
+ * Common methods and constants for the various ICU formatters used to support
+ * android.text.format.DateUtils.
+ */
+public final class DateUtilsBridge {
+ // These are all public API in DateUtils. There are others, but they're either for use with
+ // other methods (like FORMAT_ABBREV_RELATIVE), don't internationalize (like FORMAT_CAP_AMPM),
+ // or have never been implemented anyway.
+ public static final int FORMAT_SHOW_TIME = 0x00001;
+ public static final int FORMAT_SHOW_WEEKDAY = 0x00002;
+ public static final int FORMAT_SHOW_YEAR = 0x00004;
+ public static final int FORMAT_NO_YEAR = 0x00008;
+ public static final int FORMAT_SHOW_DATE = 0x00010;
+ public static final int FORMAT_NO_MONTH_DAY = 0x00020;
+ public static final int FORMAT_12HOUR = 0x00040;
+ public static final int FORMAT_24HOUR = 0x00080;
+ public static final int FORMAT_UTC = 0x02000;
+ public static final int FORMAT_ABBREV_TIME = 0x04000;
+ public static final int FORMAT_ABBREV_WEEKDAY = 0x08000;
+ public static final int FORMAT_ABBREV_MONTH = 0x10000;
+ public static final int FORMAT_NUMERIC_DATE = 0x20000;
+ public static final int FORMAT_ABBREV_RELATIVE = 0x40000;
+ public static final int FORMAT_ABBREV_ALL = 0x80000;
+
+ /**
+ * Creates an immutable ICU timezone backed by the specified libcore timezone data. At the time of
+ * writing the libcore implementation is faster but restricted to 1902 - 2038.
+ * Callers must not modify the {@code tz} after calling this method.
+ */
+ public static com.ibm.icu.util.TimeZone icuTimeZone(java.util.TimeZone tz) {
+ JavaTimeZone javaTimeZone = new JavaTimeZone(tz, null);
+ javaTimeZone.freeze(); // Optimization - allows the timezone to be copied cheaply.
+ return javaTimeZone;
+ }
+
+ public static Calendar createIcuCalendar(com.ibm.icu.util.TimeZone icuTimeZone, ULocale icuLocale,
+ long timeInMillis) {
+ Calendar calendar = new GregorianCalendar(icuTimeZone, icuLocale);
+ calendar.setTimeInMillis(timeInMillis);
+ return calendar;
+ }
+
+ public static String toSkeleton(Calendar calendar, int flags) {
+ return toSkeleton(calendar, calendar, flags);
+ }
+
+ public static String toSkeleton(Calendar startCalendar, Calendar endCalendar, int flags) {
+ if ((flags & FORMAT_ABBREV_ALL) != 0) {
+ flags |= FORMAT_ABBREV_MONTH | FORMAT_ABBREV_TIME | FORMAT_ABBREV_WEEKDAY;
+ }
+
+ String monthPart = "MMMM";
+ if ((flags & FORMAT_NUMERIC_DATE) != 0) {
+ monthPart = "M";
+ } else if ((flags & FORMAT_ABBREV_MONTH) != 0) {
+ monthPart = "MMM";
+ }
+
+ String weekPart = "EEEE";
+ if ((flags & FORMAT_ABBREV_WEEKDAY) != 0) {
+ weekPart = "EEE";
+ }
+
+ String timePart = "j"; // "j" means choose 12 or 24 hour based on current locale.
+ if ((flags & FORMAT_24HOUR) != 0) {
+ timePart = "H";
+ } else if ((flags & FORMAT_12HOUR) != 0) {
+ timePart = "h";
+ }
+
+ // If we've not been asked to abbreviate times, or we're using the 24-hour clock (where it
+ // never makes sense to leave out the minutes), include minutes. This gets us times like
+ // "4 PM" while avoiding times like "16" (for "16:00").
+ if ((flags & FORMAT_ABBREV_TIME) == 0 || (flags & FORMAT_24HOUR) != 0) {
+ timePart += "m";
+ } else {
+ // Otherwise, we're abbreviating a 12-hour time, and should only show the minutes
+ // if they're not both "00".
+ if (!(onTheHour(startCalendar) && onTheHour(endCalendar))) {
+ timePart = timePart + "m";
+ }
+ }
+
+ if (fallOnDifferentDates(startCalendar, endCalendar)) {
+ flags |= FORMAT_SHOW_DATE;
+ }
+
+ if (fallInSameMonth(startCalendar, endCalendar) && (flags & FORMAT_NO_MONTH_DAY) != 0) {
+ flags &= (~FORMAT_SHOW_WEEKDAY);
+ flags &= (~FORMAT_SHOW_TIME);
+ }
+
+ if ((flags & (FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_SHOW_WEEKDAY)) == 0) {
+ flags |= FORMAT_SHOW_DATE;
+ }
+
+ // If we've been asked to show the date, work out whether we think we should show the year.
+ if ((flags & FORMAT_SHOW_DATE) != 0) {
+ if ((flags & FORMAT_SHOW_YEAR) != 0) {
+ // The caller explicitly wants us to show the year.
+ } else if ((flags & FORMAT_NO_YEAR) != 0) {
+ // The caller explicitly doesn't want us to show the year, even if we otherwise would.
+ } else if (!fallInSameYear(startCalendar, endCalendar) || !isThisYear(startCalendar)) {
+ flags |= FORMAT_SHOW_YEAR;
+ }
+ }
+
+ StringBuilder builder = new StringBuilder();
+ if ((flags & (FORMAT_SHOW_DATE | FORMAT_NO_MONTH_DAY)) != 0) {
+ if ((flags & FORMAT_SHOW_YEAR) != 0) {
+ builder.append("y");
+ }
+ builder.append(monthPart);
+ if ((flags & FORMAT_NO_MONTH_DAY) == 0) {
+ builder.append("d");
+ }
+ }
+ if ((flags & FORMAT_SHOW_WEEKDAY) != 0) {
+ builder.append(weekPart);
+ }
+ if ((flags & FORMAT_SHOW_TIME) != 0) {
+ builder.append(timePart);
+ }
+ return builder.toString();
+ }
+
+ public static int dayDistance(Calendar c1, Calendar c2) {
+ return c2.get(Calendar.JULIAN_DAY) - c1.get(Calendar.JULIAN_DAY);
+ }
+
+ private static boolean onTheHour(Calendar c) {
+ return c.get(Calendar.MINUTE) == 0 && c.get(Calendar.SECOND) == 0;
+ }
+
+ private static boolean fallOnDifferentDates(Calendar c1, Calendar c2) {
+ return c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR) ||
+ c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH) ||
+ c1.get(Calendar.DAY_OF_MONTH) != c2.get(Calendar.DAY_OF_MONTH);
+ }
+
+ private static boolean fallInSameMonth(Calendar c1, Calendar c2) {
+ return c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH);
+ }
+
+ private static boolean fallInSameYear(Calendar c1, Calendar c2) {
+ return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR);
+ }
+
+ private static boolean isThisYear(Calendar c) {
+ Calendar now = (Calendar) c.clone();
+ now.setTimeInMillis(System.currentTimeMillis());
+ return c.get(Calendar.YEAR) == now.get(Calendar.YEAR);
+ }
+}
diff --git a/luni/src/main/java/libcore/icu/ICU.java b/luni/src/main/java/libcore/icu/ICU.java
index 0ef3f93..42def54 100644
--- a/luni/src/main/java/libcore/icu/ICU.java
+++ b/luni/src/main/java/libcore/icu/ICU.java
@@ -457,4 +457,7 @@ public final class ICU {
* Returns a locale name, not a BCP-47 language tag. e.g. en_US not en-US.
*/
public static native String getDefaultLocale();
+
+ /** Returns the TZData version as reported by ICU4C. */
+ public static native String getTZDataVersion();
}
diff --git a/luni/src/main/java/libcore/icu/LocaleData.java b/luni/src/main/java/libcore/icu/LocaleData.java
index cca38e1..cf52b9c 100644
--- a/luni/src/main/java/libcore/icu/LocaleData.java
+++ b/luni/src/main/java/libcore/icu/LocaleData.java
@@ -88,15 +88,12 @@ public final class LocaleData {
public String shortDateFormat4;
// Used by DateFormat to implement 12- and 24-hour SHORT and MEDIUM.
+ // The first two are also used directly by frameworks code.
public String timeFormat_hm;
public String timeFormat_Hm;
public String timeFormat_hms;
public String timeFormat_Hms;
- // Used by android.text.format.DateFormat.getTimeFormat.
- public String timeFormat12; // "hh:mm a"
- public String timeFormat24; // "HH:mm"
-
// Used by DecimalFormatSymbols.
public char zeroDigit;
public char decimalSeparator;
@@ -213,12 +210,6 @@ public final class LocaleData {
localeData.timeFormat_Hm = ICU.getBestDateTimePattern("Hm", locale);
localeData.timeFormat_hms = ICU.getBestDateTimePattern("hms", locale);
localeData.timeFormat_Hms = ICU.getBestDateTimePattern("Hms", locale);
- // We could move callers over to the other fields, but these seem simpler and discourage
- // people from shooting themselves in the foot by learning about patterns and skeletons.
- // TODO: the right fix here is probably to move callers over to java.text.DateFormat,
- // so nothing outside libcore references these any more.
- localeData.timeFormat12 = localeData.timeFormat_hm;
- localeData.timeFormat24 = localeData.timeFormat_Hm;
// Fix up a couple of patterns.
if (localeData.fullTimeFormat != null) {
diff --git a/luni/src/main/java/libcore/icu/NativeBreakIterator.java b/luni/src/main/java/libcore/icu/NativeBreakIterator.java
deleted file mode 100644
index 992aac2..0000000
--- a/luni/src/main/java/libcore/icu/NativeBreakIterator.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.icu;
-
-import java.text.CharacterIterator;
-import java.text.StringCharacterIterator;
-import java.util.Locale;
-
-public final class NativeBreakIterator implements Cloneable {
- // Acceptable values for the 'type' field.
- private static final int BI_CHAR_INSTANCE = 1;
- private static final int BI_WORD_INSTANCE = 2;
- private static final int BI_LINE_INSTANCE = 3;
- private static final int BI_SENT_INSTANCE = 4;
-
- // The address of the native peer.
- // Uses of this must be manually synchronized to avoid native crashes.
- private final long address;
-
- private final int type;
- private String string;
- private CharacterIterator charIterator;
-
- private NativeBreakIterator(long address, int type) {
- this.address = address;
- this.type = type;
- this.charIterator = new StringCharacterIterator("");
- }
-
- @Override
- public Object clone() {
- long cloneAddr = cloneImpl(this.address);
- NativeBreakIterator clone = new NativeBreakIterator(cloneAddr, this.type);
- clone.string = this.string;
- // The RI doesn't clone the CharacterIterator.
- clone.charIterator = this.charIterator;
- return clone;
- }
-
- @Override
- public boolean equals(Object object) {
- if (object == this) {
- return true;
- }
- if (!(object instanceof NativeBreakIterator)) {
- return false;
- }
- // TODO: is this sufficient? shouldn't we be checking the underlying rules?
- NativeBreakIterator rhs = (NativeBreakIterator) object;
- return type == rhs.type && charIterator.equals(rhs.charIterator);
- }
-
- @Override
- public int hashCode() {
- return 42; // No-one uses BreakIterator as a hash key.
- }
-
- @Override protected void finalize() throws Throwable {
- try {
- closeImpl(this.address);
- } finally {
- super.finalize();
- }
- }
-
- public int current() {
- return currentImpl(this.address, this.string);
- }
-
- public int first() {
- return firstImpl(this.address, this.string);
- }
-
- public int following(int offset) {
- return followingImpl(this.address, this.string, offset);
- }
-
- public CharacterIterator getText() {
- int newLocation = currentImpl(this.address, this.string);
- this.charIterator.setIndex(newLocation);
- return this.charIterator;
- }
-
- public int last() {
- return lastImpl(this.address, this.string);
- }
-
- public int next(int n) {
- return nextImpl(this.address, this.string, n);
- }
-
- public int next() {
- return nextImpl(this.address, this.string, 1);
- }
-
- public int previous() {
- return previousImpl(this.address, this.string);
- }
-
- public void setText(CharacterIterator newText) {
- StringBuilder sb = new StringBuilder();
- for (char c = newText.first(); c != CharacterIterator.DONE; c = newText.next()) {
- sb.append(c);
- }
- setText(sb.toString(), newText);
- }
-
- public void setText(String newText) {
- setText(newText, new StringCharacterIterator(newText));
- }
-
- private void setText(String s, CharacterIterator it) {
- this.string = s;
- this.charIterator = it;
- setTextImpl(this.address, this.string);
- }
-
- public boolean hasText() {
- return (string != null);
- }
-
- public boolean isBoundary(int offset) {
- return isBoundaryImpl(this.address, this.string, offset);
- }
-
- public int preceding(int offset) {
- return precedingImpl(this.address, this.string, offset);
- }
-
- public static NativeBreakIterator getCharacterInstance(Locale locale) {
- return new NativeBreakIterator(getCharacterInstanceImpl(locale.toLanguageTag()), BI_CHAR_INSTANCE);
- }
-
- public static NativeBreakIterator getLineInstance(Locale locale) {
- return new NativeBreakIterator(getLineInstanceImpl(locale.toLanguageTag()), BI_LINE_INSTANCE);
- }
-
- public static NativeBreakIterator getSentenceInstance(Locale locale) {
- return new NativeBreakIterator(getSentenceInstanceImpl(locale.toLanguageTag()), BI_SENT_INSTANCE);
- }
-
- public static NativeBreakIterator getWordInstance(Locale locale) {
- return new NativeBreakIterator(getWordInstanceImpl(locale.toLanguageTag()), BI_WORD_INSTANCE);
- }
-
- private static native long getCharacterInstanceImpl(String locale);
- private static native long getWordInstanceImpl(String locale);
- private static native long getLineInstanceImpl(String locale);
- private static native long getSentenceInstanceImpl(String locale);
- private static synchronized native long cloneImpl(long address);
-
- private static synchronized native void closeImpl(long address);
-
- private static synchronized native void setTextImpl(long address, String text);
- private static synchronized native int precedingImpl(long address, String text, int offset);
- private static synchronized native boolean isBoundaryImpl(long address, String text, int offset);
- private static synchronized native int nextImpl(long address, String text, int n);
- private static synchronized native int previousImpl(long address, String text);
- private static synchronized native int currentImpl(long address, String text);
- private static synchronized native int firstImpl(long address, String text);
- private static synchronized native int followingImpl(long address, String text, int offset);
- private static synchronized native int lastImpl(long address, String text);
-}
diff --git a/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java b/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java
new file mode 100644
index 0000000..e2afa61
--- /dev/null
+++ b/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.icu;
+
+import java.util.Locale;
+import libcore.util.BasicLruCache;
+
+import com.ibm.icu.text.DisplayContext;
+import com.ibm.icu.util.Calendar;
+import com.ibm.icu.util.ULocale;
+
+import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_ALL;
+import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_MONTH;
+import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_RELATIVE;
+import static libcore.icu.DateUtilsBridge.FORMAT_NO_YEAR;
+import static libcore.icu.DateUtilsBridge.FORMAT_NUMERIC_DATE;
+import static libcore.icu.DateUtilsBridge.FORMAT_SHOW_DATE;
+import static libcore.icu.DateUtilsBridge.FORMAT_SHOW_TIME;
+import static libcore.icu.DateUtilsBridge.FORMAT_SHOW_YEAR;
+
+/**
+ * Exposes icu4j's RelativeDateTimeFormatter.
+ */
+public final class RelativeDateTimeFormatter {
+
+ public static final long SECOND_IN_MILLIS = 1000;
+ public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
+ public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;
+ public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
+ public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
+ // YEAR_IN_MILLIS considers 364 days as a year. However, since this
+ // constant comes from public API in DateUtils, it cannot be fixed here.
+ public static final long YEAR_IN_MILLIS = WEEK_IN_MILLIS * 52;
+
+ private static final int DAY_IN_MS = 24 * 60 * 60 * 1000;
+ private static final int EPOCH_JULIAN_DAY = 2440588;
+
+ private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();
+
+ static class FormatterCache
+ extends BasicLruCache<String, com.ibm.icu.text.RelativeDateTimeFormatter> {
+ FormatterCache() {
+ super(8);
+ }
+ }
+
+ private RelativeDateTimeFormatter() {
+ }
+
+ /**
+ * This is the internal API that implements the functionality of
+ * DateUtils.getRelativeTimeSpanString(long, long, long, int), which is to
+ * return a string describing 'time' as a time relative to 'now' such as
+ * '5 minutes ago', or 'in 2 days'. More examples can be found in DateUtils'
+ * doc.
+ *
+ * In the implementation below, it selects the appropriate time unit based on
+ * the elapsed time between time' and 'now', e.g. minutes, days and etc.
+ * Callers may also specify the desired minimum resolution to show in the
+ * result. For example, '45 minutes ago' will become '0 hours ago' when
+ * minResolution is HOUR_IN_MILLIS. Once getting the quantity and unit to
+ * display, it calls icu4j's RelativeDateTimeFormatter to format the actual
+ * string according to the given locale.
+ *
+ * Note that when minResolution is set to DAY_IN_MILLIS, it returns the
+ * result depending on the actual date difference. For example, it will
+ * return 'Yesterday' even if 'time' was less than 24 hours ago but falling
+ * onto a different calendar day.
+ *
+ * It takes two additional parameters of Locale and TimeZone than the
+ * DateUtils' API. Caller must specify the locale and timezone.
+ * FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set in 'flags' to get
+ * the abbreviated forms when available. When 'time' equals to 'now', it
+ * always // returns a string like '0 seconds/minutes/... ago' according to
+ * minResolution.
+ */
+ public static String getRelativeTimeSpanString(Locale locale, java.util.TimeZone tz, long time,
+ long now, long minResolution, int flags) {
+ // Android has been inconsistent about capitalization in the past. e.g. bug http://b/20247811.
+ // Now we capitalize everything consistently.
+ final DisplayContext displayContext = DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE;
+ return getRelativeTimeSpanString(locale, tz, time, now, minResolution, flags, displayContext);
+ }
+
+ public static String getRelativeTimeSpanString(Locale locale, java.util.TimeZone tz, long time,
+ long now, long minResolution, int flags, DisplayContext displayContext) {
+ if (locale == null) {
+ throw new NullPointerException("locale == null");
+ }
+ if (tz == null) {
+ throw new NullPointerException("tz == null");
+ }
+ ULocale icuLocale = ULocale.forLocale(locale);
+ com.ibm.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
+ return getRelativeTimeSpanString(icuLocale, icuTimeZone, time, now, minResolution, flags,
+ displayContext);
+ }
+
+ private static String getRelativeTimeSpanString(ULocale icuLocale,
+ com.ibm.icu.util.TimeZone icuTimeZone, long time, long now, long minResolution, int flags,
+ DisplayContext displayContext) {
+
+ long duration = Math.abs(now - time);
+ boolean past = (now >= time);
+
+ com.ibm.icu.text.RelativeDateTimeFormatter.Style style;
+ if ((flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0) {
+ style = com.ibm.icu.text.RelativeDateTimeFormatter.Style.SHORT;
+ } else {
+ style = com.ibm.icu.text.RelativeDateTimeFormatter.Style.LONG;
+ }
+
+ com.ibm.icu.text.RelativeDateTimeFormatter.Direction direction;
+ if (past) {
+ direction = com.ibm.icu.text.RelativeDateTimeFormatter.Direction.LAST;
+ } else {
+ direction = com.ibm.icu.text.RelativeDateTimeFormatter.Direction.NEXT;
+ }
+
+ // 'relative' defaults to true as we are generating relative time span
+ // string. It will be set to false when we try to display strings without
+ // a quantity, such as 'Yesterday', etc.
+ boolean relative = true;
+ int count;
+ com.ibm.icu.text.RelativeDateTimeFormatter.RelativeUnit unit;
+ com.ibm.icu.text.RelativeDateTimeFormatter.AbsoluteUnit aunit = null;
+
+ if (duration < MINUTE_IN_MILLIS && minResolution < MINUTE_IN_MILLIS) {
+ count = (int)(duration / SECOND_IN_MILLIS);
+ unit = com.ibm.icu.text.RelativeDateTimeFormatter.RelativeUnit.SECONDS;
+ } else if (duration < HOUR_IN_MILLIS && minResolution < HOUR_IN_MILLIS) {
+ count = (int)(duration / MINUTE_IN_MILLIS);
+ unit = com.ibm.icu.text.RelativeDateTimeFormatter.RelativeUnit.MINUTES;
+ } else if (duration < DAY_IN_MILLIS && minResolution < DAY_IN_MILLIS) {
+ // Even if 'time' actually happened yesterday, we don't format it as
+ // "Yesterday" in this case. Unless the duration is longer than a day,
+ // or minResolution is specified as DAY_IN_MILLIS by user.
+ count = (int)(duration / HOUR_IN_MILLIS);
+ unit = com.ibm.icu.text.RelativeDateTimeFormatter.RelativeUnit.HOURS;
+ } else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) {
+ count = Math.abs(dayDistance(icuTimeZone, time, now));
+ unit = com.ibm.icu.text.RelativeDateTimeFormatter.RelativeUnit.DAYS;
+
+ if (count == 2) {
+ // Some locales have special terms for "2 days ago". Return them if
+ // available. Note that we cannot set up direction and unit here and
+ // make it fall through to use the call near the end of the function,
+ // because for locales that don't have special terms for "2 days ago",
+ // icu4j returns an empty string instead of falling back to strings
+ // like "2 days ago".
+ String str;
+ if (past) {
+ synchronized (CACHED_FORMATTERS) {
+ str = getFormatter(icuLocale, style, displayContext)
+ .format(
+ com.ibm.icu.text.RelativeDateTimeFormatter.Direction.LAST_2,
+ com.ibm.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY);
+ }
+ } else {
+ synchronized (CACHED_FORMATTERS) {
+ str = getFormatter(icuLocale, style, displayContext)
+ .format(
+ com.ibm.icu.text.RelativeDateTimeFormatter.Direction.NEXT_2,
+ com.ibm.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY);
+ }
+ }
+ if (str != null && !str.isEmpty()) {
+ return str;
+ }
+ // Fall back to show something like "2 days ago".
+ } else if (count == 1) {
+ // Show "Yesterday / Tomorrow" instead of "1 day ago / In 1 day".
+ aunit = com.ibm.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY;
+ relative = false;
+ } else if (count == 0) {
+ // Show "Today" if time and now are on the same day.
+ aunit = com.ibm.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY;
+ direction = com.ibm.icu.text.RelativeDateTimeFormatter.Direction.THIS;
+ relative = false;
+ }
+ } else if (minResolution == WEEK_IN_MILLIS) {
+ count = (int)(duration / WEEK_IN_MILLIS);
+ unit = com.ibm.icu.text.RelativeDateTimeFormatter.RelativeUnit.WEEKS;
+ } else {
+ Calendar timeCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, time);
+ // The duration is longer than a week and minResolution is not
+ // WEEK_IN_MILLIS. Return the absolute date instead of relative time.
+
+ // Bug 19822016:
+ // If user doesn't supply the year display flag, we need to explicitly
+ // set that to show / hide the year based on time and now. Otherwise
+ // formatDateRange() would determine that based on the current system
+ // time and may give wrong results.
+ if ((flags & (FORMAT_NO_YEAR | FORMAT_SHOW_YEAR)) == 0) {
+ Calendar nowCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, now);
+
+ if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) {
+ flags |= FORMAT_SHOW_YEAR;
+ } else {
+ flags |= FORMAT_NO_YEAR;
+ }
+ }
+ return DateTimeFormat.format(icuLocale, timeCalendar, flags, displayContext);
+ }
+
+ synchronized (CACHED_FORMATTERS) {
+ com.ibm.icu.text.RelativeDateTimeFormatter formatter =
+ getFormatter(icuLocale, style, displayContext);
+ if (relative) {
+ return formatter.format(count, direction, unit);
+ } else {
+ return formatter.format(direction, aunit);
+ }
+ }
+ }
+
+ /**
+ * This is the internal API that implements
+ * DateUtils.getRelativeDateTimeString(long, long, long, long, int), which is
+ * to return a string describing 'time' as a time relative to 'now', formatted
+ * like '[relative time/date], [time]'. More examples can be found in
+ * DateUtils' doc.
+ *
+ * The function is similar to getRelativeTimeSpanString, but it always
+ * appends the absolute time to the relative time string to return
+ * '[relative time/date clause], [absolute time clause]'. It also takes an
+ * extra parameter transitionResolution to determine the format of the date
+ * clause. When the elapsed time is less than the transition resolution, it
+ * displays the relative time string. Otherwise, it gives the absolute
+ * numeric date string as the date clause. With the date and time clauses, it
+ * relies on icu4j's RelativeDateTimeFormatter::combineDateAndTime() to
+ * concatenate the two.
+ *
+ * It takes two additional parameters of Locale and TimeZone than the
+ * DateUtils' API. Caller must specify the locale and timezone.
+ * FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set in 'flags' to get
+ * the abbreviated forms when they are available.
+ *
+ * Bug 5252772: Since the absolute time will always be part of the result,
+ * minResolution will be set to at least DAY_IN_MILLIS to correctly indicate
+ * the date difference. For example, when it's 1:30 AM, it will return
+ * 'Yesterday, 11:30 PM' for getRelativeDateTimeString(null, null,
+ * now - 2 hours, now, HOUR_IN_MILLIS, DAY_IN_MILLIS, 0), instead of '2
+ * hours ago, 11:30 PM' even with minResolution being HOUR_IN_MILLIS.
+ */
+ public static String getRelativeDateTimeString(Locale locale, java.util.TimeZone tz, long time,
+ long now, long minResolution, long transitionResolution, int flags) {
+
+ if (locale == null) {
+ throw new NullPointerException("locale == null");
+ }
+ if (tz == null) {
+ throw new NullPointerException("tz == null");
+ }
+ ULocale icuLocale = ULocale.forLocale(locale);
+ com.ibm.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
+
+ long duration = Math.abs(now - time);
+ // It doesn't make much sense to have results like: "1 week ago, 10:50 AM".
+ if (transitionResolution > WEEK_IN_MILLIS) {
+ transitionResolution = WEEK_IN_MILLIS;
+ }
+ com.ibm.icu.text.RelativeDateTimeFormatter.Style style;
+ if ((flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0) {
+ style = com.ibm.icu.text.RelativeDateTimeFormatter.Style.SHORT;
+ } else {
+ style = com.ibm.icu.text.RelativeDateTimeFormatter.Style.LONG;
+ }
+
+ Calendar timeCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, time);
+ Calendar nowCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, now);
+
+ int days = Math.abs(DateUtilsBridge.dayDistance(timeCalendar, nowCalendar));
+
+ // Now get the date clause, either in relative format or the actual date.
+ String dateClause;
+ if (duration < transitionResolution) {
+ // This is to fix bug 5252772. If there is any date difference, we should
+ // promote the minResolution to DAY_IN_MILLIS so that it can display the
+ // date instead of "x hours/minutes ago, [time]".
+ if (days > 0 && minResolution < DAY_IN_MILLIS) {
+ minResolution = DAY_IN_MILLIS;
+ }
+ dateClause = getRelativeTimeSpanString(icuLocale, icuTimeZone, time, now, minResolution,
+ flags, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
+ } else {
+ // We always use fixed flags to format the date clause. User-supplied
+ // flags are ignored.
+ if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) {
+ // Different years
+ flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE;
+ } else {
+ // Default
+ flags = FORMAT_SHOW_DATE | FORMAT_NO_YEAR | FORMAT_ABBREV_MONTH;
+ }
+
+ dateClause = DateTimeFormat.format(icuLocale, timeCalendar, flags,
+ DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
+ }
+
+ String timeClause = DateTimeFormat.format(icuLocale, timeCalendar, FORMAT_SHOW_TIME,
+ DisplayContext.CAPITALIZATION_NONE);
+
+ // icu4j also has other options available to control the capitalization. We are currently using
+ // the _NONE option only.
+ DisplayContext capitalizationContext = DisplayContext.CAPITALIZATION_NONE;
+
+ // Combine the two clauses, such as '5 days ago, 10:50 AM'.
+ synchronized (CACHED_FORMATTERS) {
+ return getFormatter(icuLocale, style, capitalizationContext)
+ .combineDateAndTime(dateClause, timeClause);
+ }
+ }
+
+ /**
+ * getFormatter() caches the RelativeDateTimeFormatter instances based on
+ * the combination of localeName, sytle and capitalizationContext. It
+ * should always be used along with the action of the formatter in a
+ * synchronized block, because otherwise the formatter returned by
+ * getFormatter() may have been evicted by the time of the call to
+ * formatter->action().
+ */
+ private static com.ibm.icu.text.RelativeDateTimeFormatter getFormatter(
+ ULocale locale, com.ibm.icu.text.RelativeDateTimeFormatter.Style style,
+ DisplayContext displayContext) {
+ String key = locale + "\t" + style + "\t" + displayContext;
+ com.ibm.icu.text.RelativeDateTimeFormatter formatter = CACHED_FORMATTERS.get(key);
+ if (formatter == null) {
+ formatter = com.ibm.icu.text.RelativeDateTimeFormatter.getInstance(
+ locale, null, style, displayContext);
+ CACHED_FORMATTERS.put(key, formatter);
+ }
+ return formatter;
+ }
+
+ // Return the date difference for the two times in a given timezone.
+ private static int dayDistance(com.ibm.icu.util.TimeZone icuTimeZone, long startTime,
+ long endTime) {
+ return julianDay(icuTimeZone, endTime) - julianDay(icuTimeZone, startTime);
+ }
+
+ private static int julianDay(com.ibm.icu.util.TimeZone icuTimeZone, long time) {
+ long utcMs = time + icuTimeZone.getOffset(time);
+ return (int) (utcMs / DAY_IN_MS) + EPOCH_JULIAN_DAY;
+ }
+}
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
index b3dc74b..532493a 100644
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -83,7 +83,7 @@ public class BlockGuardOs extends ForwardingOs {
// The usual case is that this _isn't_ a socket, so the getsockopt(2) call in
// isLingerSocket will throw, and that's really expensive. Try to avoid asking
// if we don't care.
- if (fd.isSocket()) {
+ if (fd.isSocket$()) {
if (isLingerSocket(fd)) {
// If the fd is a socket with SO_LINGER set, we might block indefinitely.
// We allow non-linger sockets so that apps can close their network
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index 584fd58..5c0c8fd 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -54,10 +54,12 @@ public class ForwardingOs implements Os {
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 bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { os.bind(fd, address); }
public void chmod(String path, int mode) throws ErrnoException { os.chmod(path, mode); }
public void chown(String path, int uid, int gid) throws ErrnoException { os.chown(path, uid, gid); }
public void close(FileDescriptor fd) throws ErrnoException { os.close(fd); }
public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.connect(fd, address, port); }
+ public void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { os.connect(fd, address); }
public FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException { return os.dup(oldFd); }
public FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException { return os.dup2(oldFd, newFd); }
public String[] environ() { return os.environ(); }
@@ -65,9 +67,9 @@ public class ForwardingOs implements Os {
public void execve(String filename, String[] argv, String[] envp) throws ErrnoException { os.execve(filename, argv, envp); }
public void fchmod(FileDescriptor fd, int mode) throws ErrnoException { os.fchmod(fd, mode); }
public void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException { os.fchown(fd, uid, gid); }
- public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException { return os.fcntlVoid(fd, cmd); }
- public int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException { return os.fcntlLong(fd, cmd, arg); }
public int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException, InterruptedIOException { return os.fcntlFlock(fd, cmd, arg); }
+ public int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException { return os.fcntlInt(fd, cmd, arg); }
+ public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException { return os.fcntlVoid(fd, cmd); }
public void fdatasync(FileDescriptor fd) throws ErrnoException { os.fdatasync(fd); }
public StructStat fstat(FileDescriptor fd) throws ErrnoException { return os.fstat(fd); }
public StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException { return os.fstatvfs(fd); }
@@ -80,6 +82,7 @@ public class ForwardingOs implements Os {
public String getenv(String name) { return os.getenv(name); }
public String getnameinfo(InetAddress address, int flags) throws GaiException { return os.getnameinfo(address, flags); }
public SocketAddress getpeername(FileDescriptor fd) throws ErrnoException { return os.getpeername(fd); }
+ public int getpgid(int pid) throws ErrnoException { return os.getpgid(pid); }
public int getpid() { return os.getpid(); }
public int getppid() { return os.getppid(); }
public StructPasswd getpwnam(String name) throws ErrnoException { return os.getpwnam(name); }
@@ -113,7 +116,7 @@ public class ForwardingOs implements Os {
public void munlock(long address, long byteCount) throws ErrnoException { os.munlock(address, byteCount); }
public void munmap(long address, long byteCount) throws ErrnoException { os.munmap(address, byteCount); }
public FileDescriptor open(String path, int flags, int mode) throws ErrnoException { return os.open(path, flags, mode); }
- public FileDescriptor[] pipe() throws ErrnoException { return os.pipe(); }
+ public FileDescriptor[] pipe2(int flags) throws ErrnoException { return os.pipe2(flags); }
public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException { return os.poll(fds, timeoutMs); }
public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException { os.posix_fallocate(fd, offset, length); }
public int prctl(int option, long arg2, long arg3, long arg4, long arg5) throws ErrnoException { return os.prctl(option, arg2, arg3, arg4, arg5); };
@@ -132,10 +135,14 @@ public class ForwardingOs implements Os {
public long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException { return os.sendfile(outFd, inFd, inOffset, byteCount); }
public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, buffer, flags, inetAddress, port); }
public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); }
+ public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, address); }
public void setegid(int egid) throws ErrnoException { os.setegid(egid); }
public void setenv(String name, String value, boolean overwrite) throws ErrnoException { os.setenv(name, value, overwrite); }
public void seteuid(int euid) throws ErrnoException { os.seteuid(euid); }
public void setgid(int gid) throws ErrnoException { os.setgid(gid); }
+ public void setpgid(int pid, int pgid) throws ErrnoException { os.setpgid(pid, pgid); }
+ public void setregid(int rgid, int egid) throws ErrnoException { os.setregid(rgid, egid); }
+ public void setreuid(int ruid, int euid) throws ErrnoException { os.setregid(ruid, euid); }
public int setsid() throws ErrnoException { return os.setsid(); }
public void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException { os.setsockoptByte(fd, level, option, value); }
public void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException { os.setsockoptIfreq(fd, level, option, value); }
diff --git a/luni/src/main/java/libcore/io/IoBridge.java b/luni/src/main/java/libcore/io/IoBridge.java
index acc8d4f..fcb30dd 100644
--- a/luni/src/main/java/libcore/io/IoBridge.java
+++ b/luni/src/main/java/libcore/io/IoBridge.java
@@ -225,11 +225,7 @@ public final class IoBridge {
if (!fd.valid()) {
throw new SocketException("Socket closed");
}
- if (errnoException.errno == EINTR) {
- return false; // Punt and ask the caller to try again.
- } else {
- cause = errnoException;
- }
+ cause = errnoException;
}
String detail = connectDetail(inetAddress, port, timeoutMs, cause);
if (cause.errno == ETIMEDOUT) {
diff --git a/luni/src/main/java/libcore/io/IoUtils.java b/luni/src/main/java/libcore/io/IoUtils.java
index 5a19f17..b01759d 100644
--- a/luni/src/main/java/libcore/io/IoUtils.java
+++ b/luni/src/main/java/libcore/io/IoUtils.java
@@ -30,8 +30,6 @@ import java.util.Random;
import static android.system.OsConstants.*;
public final class IoUtils {
- private static final Random TEMPORARY_DIRECTORY_PRNG = new Random();
-
private IoUtils() {
}
@@ -96,7 +94,7 @@ public final class IoUtils {
} else {
flags &= ~O_NONBLOCK;
}
- Libcore.os.fcntlLong(fd, F_SETFL, flags);
+ Libcore.os.fcntlInt(fd, F_SETFL, flags);
} catch (ErrnoException errnoException) {
throw errnoException.rethrowAsIOException();
}
@@ -142,7 +140,7 @@ public final class IoUtils {
*/
public static File createTemporaryDirectory(String prefix) {
while (true) {
- String candidateName = prefix + TEMPORARY_DIRECTORY_PRNG.nextInt();
+ String candidateName = prefix + Math.randomIntInternal();
File result = new File(System.getProperty("java.io.tmpdir"), candidateName);
if (result.mkdir()) {
return result;
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index 9f080a6..987d331 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -45,10 +45,12 @@ public interface Os {
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 bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException;
public void chmod(String path, int mode) throws ErrnoException;
public void chown(String path, int uid, int gid) throws ErrnoException;
public void close(FileDescriptor fd) throws ErrnoException;
public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
+ public void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException;
public FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException;
public FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException;
public String[] environ();
@@ -56,9 +58,9 @@ public interface Os {
public void execve(String filename, String[] argv, String[] envp) throws ErrnoException;
public void fchmod(FileDescriptor fd, int mode) throws ErrnoException;
public void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException;
- public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException;
- public int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException;
public int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException, InterruptedIOException;
+ public int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException;
+ public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException;
public void fdatasync(FileDescriptor fd) throws ErrnoException;
public StructStat fstat(FileDescriptor fd) throws ErrnoException;
public StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException;
@@ -72,6 +74,7 @@ public interface Os {
/* TODO: break into getnameinfoHost and getnameinfoService? */
public String getnameinfo(InetAddress address, int flags) throws GaiException;
public SocketAddress getpeername(FileDescriptor fd) throws ErrnoException;
+ public int getpgid(int pid) throws ErrnoException;
public int getpid();
public int getppid();
public StructPasswd getpwnam(String name) throws ErrnoException;
@@ -105,7 +108,7 @@ public interface Os {
public void munlock(long address, long byteCount) throws ErrnoException;
public void munmap(long address, long byteCount) throws ErrnoException;
public FileDescriptor open(String path, int flags, int mode) throws ErrnoException;
- public FileDescriptor[] pipe() throws ErrnoException;
+ public FileDescriptor[] pipe2(int flags) throws ErrnoException;
/* TODO: if we used the non-standard ppoll(2) behind the scenes, we could take a long timeout. */
public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException;
public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException;
@@ -124,11 +127,15 @@ public interface Os {
public void rename(String oldPath, String newPath) throws ErrnoException;
public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
+ public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException;
public long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException;
public void setegid(int egid) throws ErrnoException;
public void setenv(String name, String value, boolean overwrite) throws ErrnoException;
public void seteuid(int euid) throws ErrnoException;
public void setgid(int gid) throws ErrnoException;
+ public void setpgid(int pid, int pgid) throws ErrnoException;
+ public void setregid(int rgid, int egid) throws ErrnoException;
+ public void setreuid(int ruid, int euid) throws ErrnoException;
public int setsid() throws ErrnoException;
public void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
public void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException;
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index 5bd1b06..d680200 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -48,10 +48,12 @@ public final class Posix implements Os {
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 bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException;
public native void chmod(String path, int mode) throws ErrnoException;
public native void chown(String path, int uid, int gid) throws ErrnoException;
public native void close(FileDescriptor fd) throws ErrnoException;
public native void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
+ public native void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException;
public native FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException;
public native FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException;
public native String[] environ();
@@ -59,9 +61,9 @@ public final class Posix implements Os {
public native void execve(String filename, String[] argv, String[] envp) throws ErrnoException;
public native void fchmod(FileDescriptor fd, int mode) throws ErrnoException;
public native void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException;
- public native int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException;
- public native int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException;
public native int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException, InterruptedIOException;
+ public native int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException;
+ public native int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException;
public native void fdatasync(FileDescriptor fd) throws ErrnoException;
public native StructStat fstat(FileDescriptor fd) throws ErrnoException;
public native StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException;
@@ -74,6 +76,7 @@ public final class Posix implements Os {
public native String getenv(String name);
public native String getnameinfo(InetAddress address, int flags) throws GaiException;
public native SocketAddress getpeername(FileDescriptor fd) throws ErrnoException;
+ public native int getpgid(int pid);
public native int getpid();
public native int getppid();
public native StructPasswd getpwnam(String name) throws ErrnoException;
@@ -107,7 +110,7 @@ public final class Posix implements Os {
public native void munlock(long address, long byteCount) throws ErrnoException;
public native void munmap(long address, long byteCount) throws ErrnoException;
public native FileDescriptor open(String path, int flags, int mode) throws ErrnoException;
- public native FileDescriptor[] pipe() throws ErrnoException;
+ public native FileDescriptor[] pipe2(int flags) throws ErrnoException;
public native int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException;
public native void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException;
public native int prctl(int option, long arg2, long arg3, long arg4, long arg5) throws ErrnoException;
@@ -205,11 +208,18 @@ public final class Posix implements Os {
// This indirection isn't strictly necessary, but ensures that our public interface is type safe.
return sendtoBytes(fd, bytes, byteOffset, byteCount, flags, inetAddress, port);
}
+ public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException {
+ return sendtoBytes(fd, bytes, byteOffset, byteCount, flags, address);
+ }
private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
+ private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException;
public native void setegid(int egid) throws ErrnoException;
public native void setenv(String name, String value, boolean overwrite) throws ErrnoException;
public native void seteuid(int euid) throws ErrnoException;
public native void setgid(int gid) throws ErrnoException;
+ public native void setpgid(int pid, int pgid) throws ErrnoException;
+ public native void setregid(int rgid, int egid) throws ErrnoException;
+ public native void setreuid(int ruid, int euid) throws ErrnoException;
public native int setsid() throws ErrnoException;
public native void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
public native void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException;
diff --git a/luni/src/main/java/libcore/net/MimeUtils.java b/luni/src/main/java/libcore/net/MimeUtils.java
index 125ea87..e36b3d1 100644
--- a/luni/src/main/java/libcore/net/MimeUtils.java
+++ b/luni/src/main/java/libcore/net/MimeUtils.java
@@ -16,13 +16,8 @@
package libcore.net;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
-import java.util.Properties;
/**
* Utilities for dealing with MIME types.
@@ -49,6 +44,7 @@ public final class MimeUtils {
add("application/andrew-inset", "ez");
add("application/dsptype", "tsp");
+ add("application/epub+zip", "epub");
add("application/hta", "hta");
add("application/mac-binhex40", "hqx");
add("application/mathematica", "nb");
@@ -73,6 +69,8 @@ public final class MimeUtils {
add("application/vnd.oasis.opendocument.graphics", "odg");
add("application/vnd.oasis.opendocument.graphics-template", "otg");
add("application/vnd.oasis.opendocument.image", "odi");
+ add("application/vnd.oasis.opendocument.presentation", "odp");
+ add("application/vnd.oasis.opendocument.presentation-template", "otp");
add("application/vnd.oasis.opendocument.spreadsheet", "ods");
add("application/vnd.oasis.opendocument.spreadsheet-template", "ots");
add("application/vnd.oasis.opendocument.text", "odt");
@@ -148,6 +146,7 @@ public final class MimeUtils {
add("application/x-gtar", "gtar");
add("application/x-gtar", "taz");
add("application/x-hdf", "hdf");
+ add("application/x-hwp", "hwp"); // http://b/18788282.
add("application/x-ica", "ica");
add("application/x-internet-signup", "ins");
add("application/x-internet-signup", "isp");
@@ -378,7 +377,6 @@ public final class MimeUtils {
add("video/x-webex", "wrf");
add("x-conference/x-cooltalk", "ice");
add("x-epoc/x-sisx-app", "sisx");
- applyOverrides();
}
private static void add(String mimeType, String extension) {
@@ -395,61 +393,6 @@ public final class MimeUtils {
}
}
- private static InputStream getContentTypesPropertiesStream() {
- // User override?
- String userTable = System.getProperty("content.types.user.table");
- if (userTable != null) {
- File f = new File(userTable);
- if (f.exists()) {
- try {
- return new FileInputStream(f);
- } catch (IOException ignored) {
- }
- }
- }
-
- // Standard location?
- File f = new File(System.getProperty("java.home"), "lib" + File.separator + "content-types.properties");
- if (f.exists()) {
- try {
- return new FileInputStream(f);
- } catch (IOException ignored) {
- }
- }
-
- return null;
- }
-
- /**
- * This isn't what the RI does. The RI doesn't have hard-coded defaults, so supplying your
- * own "content.types.user.table" means you don't get any of the built-ins, and the built-ins
- * come from "$JAVA_HOME/lib/content-types.properties".
- */
- private static void applyOverrides() {
- // Get the appropriate InputStream to read overrides from, if any.
- InputStream stream = getContentTypesPropertiesStream();
- if (stream == null) {
- return;
- }
-
- try {
- try {
- // Read the properties file...
- Properties overrides = new Properties();
- overrides.load(stream);
- // And translate its mapping to ours...
- for (Map.Entry<Object, Object> entry : overrides.entrySet()) {
- String extension = (String) entry.getKey();
- String mimeType = (String) entry.getValue();
- add(mimeType, extension);
- }
- } finally {
- stream.close();
- }
- } catch (IOException ignored) {
- }
- }
-
private MimeUtils() {
}
diff --git a/luni/src/main/java/libcore/net/NetworkSecurityPolicy.java b/luni/src/main/java/libcore/net/NetworkSecurityPolicy.java
new file mode 100644
index 0000000..b1a41e8
--- /dev/null
+++ b/luni/src/main/java/libcore/net/NetworkSecurityPolicy.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.net;
+
+import libcore.net.url.FtpURLConnection;
+
+/**
+ * Network security policy for this process/application.
+ *
+ * <p>Network stacks/components are expected to honor this policy. Components which can use the
+ * Android framework API should be accessing this policy via the framework's
+ * {@code android.security.NetworkSecurityPolicy} instead of via this class.
+ *
+ * <p>The policy currently consists of a single flag: whether cleartext network traffic is
+ * permitted. See {@link #isCleartextTrafficPermitted()}.
+ */
+public class NetworkSecurityPolicy {
+
+ private static volatile boolean cleartextTrafficPermitted = true;
+
+ /**
+ * Returns whether cleartext network traffic (e.g. HTTP, FTP, XMPP, IMAP, SMTP -- without TLS or
+ * STARTTLS) is permitted for this process.
+ *
+ * <p>When cleartext network traffic is not permitted, the platform's components (e.g. HTTP
+ * stacks, {@code WebView}, {@code MediaPlayer}) will refuse this process's requests to use
+ * cleartext traffic. Third-party libraries are encouraged to do the same.
+ *
+ * <p>This flag is honored on a best effort basis because it's impossible to prevent all
+ * cleartext traffic from an application given the level of access provided to applications on
+ * Android. For example, there's no expectation that {@link java.net.Socket} API will honor this
+ * flag. Luckily, most network traffic from apps is handled by higher-level network stacks which
+ * can be made to honor this flag. Platform-provided network stacks (e.g. HTTP and FTP) honor
+ * this flag from day one, and well-established third-party network stacks will eventually
+ * honor it.
+ *
+ * <p>See {@link FtpURLConnection} for an example of honoring this flag.
+ */
+ public static boolean isCleartextTrafficPermitted() {
+ return cleartextTrafficPermitted;
+ }
+
+ /**
+ * Sets whether cleartext network traffic (e.g. HTTP, FTP, XMPP, IMAP, SMTP -- without TLS or
+ * STARTTLS) is permitted for this process.
+ *
+ * @see #isCleartextTrafficPermitted()
+ */
+ public static void setCleartextTrafficPermitted(boolean permitted) {
+ cleartextTrafficPermitted = permitted;
+ }
+}
diff --git a/luni/src/main/java/libcore/net/http/ResponseUtils.java b/luni/src/main/java/libcore/net/http/ResponseUtils.java
new file mode 100644
index 0000000..c892b53
--- /dev/null
+++ b/luni/src/main/java/libcore/net/http/ResponseUtils.java
@@ -0,0 +1,90 @@
+/*
+ * 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 libcore.net.http;
+
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @hide
+ */
+public class ResponseUtils {
+ /**
+ * Returns the response charset of a HTTP response based on the {@code Content-Type} of
+ * the response (see RFC 7230). If the {@code Content-Type} header is missing or invalid,
+ * the response is assumed to be encoded as {@code UTF-8}. Note that a charset usually
+ * makes sense only for {@code "text/plain"} and other "text based" responses.
+ *
+ * @throws IllegalCharsetNameException if the response specified charset is illegal.
+ * @throws UnsupportedCharsetException if the response specified charset is unsupported.
+ */
+ public static Charset responseCharset(String contentTypeHeader)
+ throws IllegalCharsetNameException, UnsupportedCharsetException {
+ Charset responseCharset = StandardCharsets.UTF_8;
+ if (contentTypeHeader != null) {
+ Map<String, String> contentTypeParams = parseContentTypeParameters(contentTypeHeader);
+ String charsetParameter = contentTypeParams.get("charset");
+ if (charsetParameter != null) {
+ responseCharset = Charset.forName(charsetParameter);
+ }
+ }
+
+ return responseCharset;
+ }
+
+ /**
+ * Parse content-type parameters. The format of this header is roughly :
+ * {@code type/subtype; param1=value1; param2=value2 ...} where each of the
+ * parameters are optional. Parsing is lenient, malformed parameters are ignored.
+ *
+ * Parameter keys & values are trimmed of whitespace and keys are converted to
+ * lower case.
+ */
+ private static Map<String, String> parseContentTypeParameters(String contentTypeHeader) {
+ Map<String, String> parameters = Collections.EMPTY_MAP;
+
+ String[] fields = contentTypeHeader.split(";");
+ if (fields.length > 1) {
+ parameters = new HashMap<>();
+ // Ignore the first element in the array (the type/subtype).
+ for (int i = 1; i < fields.length; ++i) {
+ final String parameter = fields[i];
+ if (!parameter.isEmpty()) {
+ final String[] components = parameter.split("=");
+ if (components.length != 2) {
+ continue;
+ }
+
+ final String key = components[0].trim().toLowerCase();
+ final String value = components[1].trim();
+ if (key.isEmpty() || value.isEmpty()) {
+ continue;
+ }
+
+ parameters.put(key, value);
+ }
+ }
+ }
+
+ return parameters;
+ }
+}
diff --git a/luni/src/main/java/libcore/net/url/FtpURLConnection.java b/luni/src/main/java/libcore/net/url/FtpURLConnection.java
index 7594c3a..021bfa2 100644
--- a/luni/src/main/java/libcore/net/url/FtpURLConnection.java
+++ b/luni/src/main/java/libcore/net/url/FtpURLConnection.java
@@ -17,6 +17,7 @@
package libcore.net.url;
+import libcore.net.NetworkSecurityPolicy;
import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.FileNotFoundException;
@@ -103,9 +104,15 @@ public class FtpURLConnection extends URLConnection {
*
* @param url
*/
- protected FtpURLConnection(URL url) {
+ protected FtpURLConnection(URL url) throws IOException {
super(url);
hostName = url.getHost();
+ if (!NetworkSecurityPolicy.isCleartextTrafficPermitted()) {
+ // Cleartext network traffic is not permitted -- refuse this connection.
+ throw new IOException("Cleartext traffic not permitted: "
+ + url.getProtocol() + "://" + hostName
+ + ((url.getPort() >= 0) ? (":" + url.getPort()) : ""));
+ }
String parse = url.getUserInfo();
if (parse != null) {
int split = parse.indexOf(':');
@@ -118,7 +125,7 @@ public class FtpURLConnection extends URLConnection {
}
uri = null;
try {
- uri = url.toURI();
+ uri = url.toURILenient();
} catch (URISyntaxException e) {
// do nothing.
}
@@ -130,7 +137,7 @@ public class FtpURLConnection extends URLConnection {
* @param url
* @param proxy
*/
- protected FtpURLConnection(URL url, Proxy proxy) {
+ protected FtpURLConnection(URL url, Proxy proxy) throws IOException {
this(url);
this.proxy = proxy;
}
diff --git a/luni/src/main/java/java/nio/charset/Charsets.java b/luni/src/main/java/libcore/util/CharsetUtils.java
index 3dede7a..2e426c4 100644
--- a/luni/src/main/java/java/nio/charset/Charsets.java
+++ b/luni/src/main/java/libcore/util/CharsetUtils.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package java.nio.charset;
+package libcore.util;
/**
* Various special-case charset conversions (for performance).
*
* @hide internal use only
*/
-public final class Charsets {
+public final class CharsetUtils {
/**
* Returns a new byte array containing the bytes corresponding to the given characters,
* encoded in US-ASCII. Unrepresentable characters are replaced by (byte) '?'.
@@ -75,6 +75,6 @@ public final class Charsets {
*/
public static native void isoLatin1BytesToChars(byte[] bytes, int offset, int length, char[] chars);
- private Charsets() {
+ private CharsetUtils() {
}
}
diff --git a/luni/src/main/java/libcore/util/CountingOutputStream.java b/luni/src/main/java/libcore/util/CountingOutputStream.java
new file mode 100644
index 0000000..cc0e1f2
--- /dev/null
+++ b/luni/src/main/java/libcore/util/CountingOutputStream.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.util;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An output stream that keeps count of the number of bytes written to it.
+ *
+ * Useful when we need to make decisions based on the size of the output, such
+ * as deciding what sort of metadata to writes to zip files.
+ */
+public class CountingOutputStream extends FilterOutputStream {
+
+ private long count;
+
+ /**
+ * Constructs a new {@code FilterOutputStream} with {@code out} as its
+ * target stream.
+ *
+ * @param out the target stream that this stream writes to.
+ */
+ public CountingOutputStream(OutputStream out) {
+ super(out);
+ count = 0;
+ }
+
+ @Override
+ public void write(byte[] buffer, int offset, int length) throws IOException {
+ out.write(buffer, offset, length);
+ count += length;
+ }
+
+ @Override
+ public void write(int oneByte) throws IOException {
+ out.write(oneByte);
+ count++;
+ }
+
+ public long getCount() {
+ return count;
+ }
+}
diff --git a/luni/src/main/java/libcore/util/HexEncoding.java b/luni/src/main/java/libcore/util/HexEncoding.java
new file mode 100644
index 0000000..f883a73
--- /dev/null
+++ b/luni/src/main/java/libcore/util/HexEncoding.java
@@ -0,0 +1,99 @@
+/*
+ * 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 libcore.util;
+
+/**
+ * Hexadecimal encoding where each byte is represented by two hexadecimal digits.
+ */
+public class HexEncoding {
+
+ /** Hidden constructor to prevent instantiation. */
+ private HexEncoding() {}
+
+ private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
+
+ /**
+ * Encodes the provided data as a sequence of hexadecimal characters.
+ */
+ public static char[] encode(byte[] data) {
+ return encode(data, 0, data.length);
+ }
+
+ /**
+ * Encodes the provided data as a sequence of hexadecimal characters.
+ */
+ public static char[] encode(byte[] data, int offset, int len) {
+ char[] result = new char[len * 2];
+ for (int i = 0; i < len; i++) {
+ byte b = data[offset + i];
+ int resultIndex = 2 * i;
+ result[resultIndex] = (HEX_DIGITS[(b >>> 4) & 0x0f]);
+ result[resultIndex + 1] = (HEX_DIGITS[b & 0x0f]);
+ }
+
+ return result;
+ }
+
+ /**
+ * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar}
+ * is {@code true} odd-length inputs are allowed and the first character is interpreted
+ * as the lower bits of the first result byte.
+ *
+ * Throws an {@code IllegalArgumentException} if the input is malformed.
+ */
+ public static byte[] decode(char[] encoded, boolean allowSingleChar) throws IllegalArgumentException {
+ int resultLengthBytes = (encoded.length + 1) / 2;
+ byte[] result = new byte[resultLengthBytes];
+
+ int resultOffset = 0;
+ int i = 0;
+ if (allowSingleChar) {
+ if ((encoded.length % 2) != 0) {
+ // Odd number of digits -- the first digit is the lower 4 bits of the first result byte.
+ result[resultOffset++] = (byte) toDigit(encoded, i);
+ i++;
+ }
+ } else {
+ if ((encoded.length % 2) != 0) {
+ throw new IllegalArgumentException("Invalid input length: " + encoded.length);
+ }
+ }
+
+ for (int len = encoded.length; i < len; i += 2) {
+ result[resultOffset++] = (byte) ((toDigit(encoded, i) << 4) | toDigit(encoded, i + 1));
+ }
+
+ return result;
+ }
+
+ private static int toDigit(char[] str, int offset) throws IllegalArgumentException {
+ // NOTE: that this isn't really a code point in the traditional sense, since we're
+ // just rejecting surrogate pairs outright.
+ int pseudoCodePoint = str[offset];
+
+ if ('0' <= pseudoCodePoint && pseudoCodePoint <= '9') {
+ return pseudoCodePoint - '0';
+ } else if ('a' <= pseudoCodePoint && pseudoCodePoint <= 'f') {
+ return 10 + (pseudoCodePoint - 'a');
+ } else if ('A' <= pseudoCodePoint && pseudoCodePoint <= 'F') {
+ return 10 + (pseudoCodePoint - 'A');
+ }
+
+ throw new IllegalArgumentException("Illegal char: " + str[offset] +
+ " at offset " + offset);
+ }
+}
diff --git a/luni/src/main/java/libcore/util/ZoneInfo.java b/luni/src/main/java/libcore/util/ZoneInfo.java
index 4d58d93..329320d 100644
--- a/luni/src/main/java/libcore/util/ZoneInfo.java
+++ b/luni/src/main/java/libcore/util/ZoneInfo.java
@@ -54,7 +54,7 @@ public final class ZoneInfo extends TimeZone {
private final boolean mUseDst;
private final int mDstSavings; // Implements TimeZone.getDSTSavings.
- private final int[] mTransitions;
+ private final long[] mTransitions;
private final int[] mOffsets;
private final byte[] mTypes;
private final byte[] mIsDsts;
@@ -76,8 +76,19 @@ public final class ZoneInfo extends TimeZone {
it.skip(4); // Skip tzh_charcnt.
- int[] transitions = new int[tzh_timecnt];
- it.readIntArray(transitions, 0, transitions.length);
+ // Transitions are signed 32 bit integers, but we store them as signed 64 bit
+ // integers since it's easier to compare them against 64 bit inputs (see getOffset
+ // and isDaylightTime) with much less risk of an overflow in our calculations.
+ //
+ // The alternative of checking the input against the first and last transition in
+ // the array is far more awkward and error prone.
+ int[] transitions32 = new int[tzh_timecnt];
+ it.readIntArray(transitions32, 0, transitions32.length);
+
+ long[] transitions64 = new long[tzh_timecnt];
+ for (int i = 0; i < tzh_timecnt; ++i) {
+ transitions64[i] = transitions32[i];
+ }
byte[] type = new byte[tzh_timecnt];
it.readByteArray(type, 0, type.length);
@@ -97,10 +108,10 @@ public final class ZoneInfo extends TimeZone {
it.skip(1);
}
- return new ZoneInfo(id, transitions, type, gmtOffsets, isDsts);
+ return new ZoneInfo(id, transitions64, type, gmtOffsets, isDsts);
}
- private ZoneInfo(String name, int[] transitions, byte[] types, int[] gmtOffsets, byte[] isDsts) {
+ private ZoneInfo(String name, long[] transitions, byte[] types, int[] gmtOffsets, byte[] isDsts) {
mTransitions = transitions;
mTypes = types;
mIsDsts = isDsts;
@@ -163,7 +174,7 @@ public final class ZoneInfo extends TimeZone {
// no future plans (and thus no future schedule info) will report "true" from
// useDaylightTime at the start of 2009 but "false" at the end. This seems appropriate.
boolean usesDst = false;
- int currentUnixTimeSeconds = (int) (System.currentTimeMillis() / 1000);
+ long currentUnixTimeSeconds = System.currentTimeMillis() / 1000;
int i = mTransitions.length - 1;
while (i >= 0 && mTransitions[i] >= currentUnixTimeSeconds) {
if (mIsDsts[mTypes[i]] > 0) {
@@ -210,7 +221,7 @@ public final class ZoneInfo extends TimeZone {
@Override
public int getOffset(long when) {
- int unix = (int) (when / 1000);
+ long unix = when / 1000;
int transition = Arrays.binarySearch(mTransitions, unix);
if (transition < 0) {
transition = ~transition - 1;
@@ -226,7 +237,7 @@ public final class ZoneInfo extends TimeZone {
@Override public boolean inDaylightTime(Date time) {
long when = time.getTime();
- int unix = (int) (when / 1000);
+ long unix = when / 1000;
int transition = Arrays.binarySearch(mTransitions, unix);
if (transition < 0) {
transition = ~transition - 1;
@@ -952,9 +963,9 @@ public final class ZoneInfo extends TimeZone {
*
* @throws CheckedArithmeticException if overflow or underflow occurs
*/
- private static int checkedAdd(int a, int b) throws CheckedArithmeticException {
+ private static int checkedAdd(long a, int b) throws CheckedArithmeticException {
// Adapted from Guava IntMath.checkedAdd();
- long result = (long) a + b;
+ long result = a + b;
if (result != (int) result) {
throw new CheckedArithmeticException();
}
diff --git a/luni/src/main/java/libcore/util/ZoneInfoDB.java b/luni/src/main/java/libcore/util/ZoneInfoDB.java
index 906ec14..b8e1dad 100644
--- a/luni/src/main/java/libcore/util/ZoneInfoDB.java
+++ b/luni/src/main/java/libcore/util/ZoneInfoDB.java
@@ -41,7 +41,8 @@ import libcore.io.MemoryMappedFile;
*/
public final class ZoneInfoDB {
private static final TzData DATA =
- new TzData(System.getenv("ANDROID_ROOT") + "/usr/share/zoneinfo/tzdata");
+ new TzData(System.getenv("ANDROID_DATA") + "/misc/zoneinfo/current/tzdata",
+ System.getenv("ANDROID_ROOT") + "/usr/share/zoneinfo/tzdata");
public static class TzData {
/**
diff --git a/luni/src/main/java/org/apache/harmony/security/fortress/Services.java b/luni/src/main/java/org/apache/harmony/security/fortress/Services.java
index 30f4839..c81bf6b 100644
--- a/luni/src/main/java/org/apache/harmony/security/fortress/Services.java
+++ b/luni/src/main/java/org/apache/harmony/security/fortress/Services.java
@@ -63,6 +63,24 @@ public class Services {
private static final ArrayList<Provider> providers = new ArrayList<Provider>(20);
/**
+ * Try to load and register a provider by name from the given class-loader.
+ */
+ private static boolean initProvider(String providerClassName, ClassLoader classLoader) {
+ try {
+ Class<?> providerClass = Class.forName(providerClassName.trim(), true, classLoader);
+ Provider p = (Provider) providerClass.newInstance();
+ providers.add(p);
+ providersNames.put(p.getName(), p);
+ initServiceInfo(p);
+ return true;
+ } catch (ClassNotFoundException ignored) {
+ } catch (IllegalAccessException ignored) {
+ } catch (InstantiationException ignored) {
+ }
+ return false;
+ }
+
+ /**
* Hash for quick provider access by name.
*/
private static final HashMap<String, Provider> providersNames
@@ -70,18 +88,16 @@ public class Services {
static {
String providerClassName = null;
int i = 1;
- ClassLoader cl = ClassLoader.getSystemClassLoader();
+ ClassLoader cl = Services.class.getClassLoader();
while ((providerClassName = Security.getProperty("security.provider." + i++)) != null) {
- try {
- Class<?> providerClass = Class.forName(providerClassName.trim(), true, cl);
- Provider p = (Provider) providerClass.newInstance();
- providers.add(p);
- providersNames.put(p.getName(), p);
- initServiceInfo(p);
- } catch (ClassNotFoundException ignored) {
- } catch (IllegalAccessException ignored) {
- } catch (InstantiationException ignored) {
+ if (!initProvider(providerClassName, cl)) {
+ // Not on the boot classpath. Try the system class-loader.
+ // Note: DO NOT USE A LOCAL FOR GETSYSTEMCLASSLOADER! This will break compile-time
+ // initialization.
+ if (!initProvider(providerClassName, ClassLoader.getSystemClassLoader())) {
+ // TODO: Logging?
+ }
}
}
Engine.door.renumProviders();
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 917a3a8..020663e 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
@@ -198,8 +198,8 @@ public class JarUtils {
}
}
- // RFC 3852 section 9.2: it authAttrs is present, it must have a
- // message digest entry.
+ // RFC 2315 section 9.1: if authenticatedAttributes is present, it
+ // must have a message-digest attribute.
if (existingDigest == null) {
throw new SecurityException("Missing MessageDigest in Authenticated Attributes");
}
diff --git a/luni/src/main/java/org/apache/harmony/security/utils/WrappedX509Certificate.java b/luni/src/main/java/org/apache/harmony/security/utils/WrappedX509Certificate.java
index 2b09309..1c07915 100644
--- a/luni/src/main/java/org/apache/harmony/security/utils/WrappedX509Certificate.java
+++ b/luni/src/main/java/org/apache/harmony/security/utils/WrappedX509Certificate.java
@@ -160,7 +160,7 @@ public class WrappedX509Certificate extends X509Certificate {
public void verify(PublicKey key, String sigProvider) throws CertificateException,
NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException,
SignatureException {
- verify(key, sigProvider);
+ wrapped.verify(key, sigProvider);
}
@Override
diff --git a/luni/src/main/native/IcuUtilities.cpp b/luni/src/main/native/IcuUtilities.cpp
index 7ce2168..98648a5 100644
--- a/luni/src/main/native/IcuUtilities.cpp
+++ b/luni/src/main/native/IcuUtilities.cpp
@@ -22,13 +22,12 @@
#include "JniException.h"
#include "ScopedLocalRef.h"
#include "ScopedUtfChars.h"
-#include "UniquePtr.h"
#include "cutils/log.h"
#include "unicode/strenum.h"
#include "unicode/uloc.h"
#include "unicode/ustring.h"
-jobjectArray fromStringEnumeration(JNIEnv* env, UErrorCode& status, const char* provider, StringEnumeration* se) {
+jobjectArray fromStringEnumeration(JNIEnv* env, UErrorCode& status, const char* provider, icu::StringEnumeration* se) {
if (maybeThrowIcuException(env, provider, status)) {
return NULL;
}
@@ -40,7 +39,7 @@ jobjectArray fromStringEnumeration(JNIEnv* env, UErrorCode& status, const char*
jobjectArray result = env->NewObjectArray(count, JniConstants::stringClass, NULL);
for (int32_t i = 0; i < count; ++i) {
- const UnicodeString* string = se->snext(status);
+ const icu::UnicodeString* string = se->snext(status);
if (maybeThrowIcuException(env, "StringEnumeration::snext", status)) {
return NULL;
}
diff --git a/luni/src/main/native/IcuUtilities.h b/luni/src/main/native/IcuUtilities.h
index 737379e..c64de30 100644
--- a/luni/src/main/native/IcuUtilities.h
+++ b/luni/src/main/native/IcuUtilities.h
@@ -17,14 +17,11 @@
#ifndef ICU_UTILITIES_H_included
#define ICU_UTILITIES_H_included
-#undef U_HAVE_STD_STRING
-#define U_HAVE_STD_STRING 1 // For UnicodeString::toUTF8String(std::string&).
-
#include "jni.h"
#include "ustrenum.h" // For UStringEnumeration.
#include "unicode/utypes.h" // For UErrorCode.
-extern jobjectArray fromStringEnumeration(JNIEnv* env, UErrorCode& status, const char* provider, StringEnumeration*);
+extern jobjectArray fromStringEnumeration(JNIEnv* env, UErrorCode& status, const char* provider, icu::StringEnumeration*);
bool maybeThrowIcuException(JNIEnv* env, const char* function, UErrorCode error);
#endif // ICU_UTILITIES_H_included
diff --git a/luni/src/main/native/Portability.h b/luni/src/main/native/Portability.h
index 1520311..5900cb0 100644
--- a/luni/src/main/native/Portability.h
+++ b/luni/src/main/native/Portability.h
@@ -65,7 +65,7 @@ static inline int mincore(void* addr, size_t length, unsigned char* vec) {
#include <sys/param.h>
#include <sys/mount.h>
-#else // defined(__APPLE__)
+#else
// Bionic or glibc.
@@ -73,15 +73,33 @@ static inline int mincore(void* addr, size_t length, unsigned char* vec) {
#include <sys/sendfile.h>
#include <sys/statvfs.h>
-#endif // defined(__APPLE__)
+#endif
-#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) {
+#if defined(__BIONIC__)
+extern "C" int android_getaddrinfofornet(const char*, const char*, const struct addrinfo*, unsigned, unsigned, struct addrinfo**);
+#else
+static 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
+
+#if defined(__GLIBC__) && !defined(__LP64__)
+
+#include <unistd.h>
+
+// 32 bit GLIBC hardcodes a "long int" as the return type for
+// TEMP_FAILURE_RETRY so the return value here gets truncated for
+// functions that return 64 bit types.
+#undef TEMP_FAILURE_RETRY
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ __typeof__(exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+
+#endif // __GLIBC__ && !__LP64__
#endif // PORTABILITY_H_included
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index 6a2c939..0f2d0ad 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -48,7 +48,6 @@ jint JNI_OnLoad(JavaVM* vm, void*) {
REGISTER(register_java_lang_System);
REGISTER(register_java_math_NativeBN);
REGISTER(register_java_nio_ByteOrder);
- REGISTER(register_java_nio_charset_Charsets);
REGISTER(register_java_text_Bidi);
REGISTER(register_java_util_jar_StrictJarFile);
REGISTER(register_java_util_regex_Matcher);
@@ -58,9 +57,7 @@ jint JNI_OnLoad(JavaVM* vm, void*) {
REGISTER(register_java_util_zip_Deflater);
REGISTER(register_java_util_zip_Inflater);
REGISTER(register_libcore_icu_AlphabeticIndex);
- REGISTER(register_libcore_icu_DateIntervalFormat);
REGISTER(register_libcore_icu_ICU);
- REGISTER(register_libcore_icu_NativeBreakIterator);
REGISTER(register_libcore_icu_NativeCollation);
REGISTER(register_libcore_icu_NativeConverter);
REGISTER(register_libcore_icu_NativeDecimalFormat);
@@ -72,6 +69,7 @@ jint JNI_OnLoad(JavaVM* vm, void*) {
REGISTER(register_libcore_io_AsynchronousCloseMonitor);
REGISTER(register_libcore_io_Memory);
REGISTER(register_libcore_io_Posix);
+ REGISTER(register_libcore_util_CharsetUtils);
REGISTER(register_org_apache_harmony_dalvik_NativeTestTarget);
REGISTER(register_org_apache_harmony_xml_ExpatParser);
REGISTER(register_sun_misc_Unsafe);
diff --git a/luni/src/main/native/ZipUtilities.cpp b/luni/src/main/native/ZipUtilities.cpp
index 745b3b1..b7d2209 100644
--- a/luni/src/main/native/ZipUtilities.cpp
+++ b/luni/src/main/native/ZipUtilities.cpp
@@ -15,9 +15,10 @@
* limitations under the License.
*/
+#include <memory>
+
#include "JniConstants.h"
#include "JniException.h"
-#include "UniquePtr.h"
#include "ZipUtilities.h"
void throwExceptionForZlibError(JNIEnv* env, const char* exceptionClassName, int error,
@@ -31,7 +32,7 @@ void throwExceptionForZlibError(JNIEnv* env, const char* exceptionClassName, int
}
}
-NativeZipStream::NativeZipStream() : input(NULL), inCap(0), mDict(NULL) {
+NativeZipStream::NativeZipStream() : inCap(0), totalIn(0), totalOut(0) {
// Let zlib use its default allocator.
stream.opaque = Z_NULL;
stream.zalloc = Z_NULL;
@@ -43,7 +44,7 @@ NativeZipStream::~NativeZipStream() {
void NativeZipStream::setDictionary(JNIEnv* env, jbyteArray javaDictionary, int off, int len,
bool inflate) {
- UniquePtr<jbyte[]> dictionaryBytes(new jbyte[len]);
+ std::unique_ptr<jbyte[]> dictionaryBytes(new jbyte[len]);
if (dictionaryBytes.get() == NULL) {
jniThrowOutOfMemoryError(env, NULL);
return;
diff --git a/luni/src/main/native/ZipUtilities.h b/luni/src/main/native/ZipUtilities.h
index fe0f977..50111a5 100644
--- a/luni/src/main/native/ZipUtilities.h
+++ b/luni/src/main/native/ZipUtilities.h
@@ -18,15 +18,18 @@
#ifndef ZIP_UTILITIES_H_included
#define ZIP_UTILITIES_H_included
-#include "UniquePtr.h"
+#include <cstdint>
+#include <memory>
#include "jni.h"
#include "zlib.h"
class NativeZipStream {
public:
- UniquePtr<jbyte[]> input;
+ std::unique_ptr<jbyte[]> input;
int inCap;
z_stream stream;
+ uint64_t totalIn;
+ uint64_t totalOut;
NativeZipStream();
~NativeZipStream();
@@ -34,7 +37,7 @@ public:
void setInput(JNIEnv* env, jbyteArray buf, jint off, jint len);
private:
- UniquePtr<jbyte[]> mDict;
+ std::unique_ptr<jbyte[]> mDict;
// Disallow copy and assignment.
NativeZipStream(const NativeZipStream&);
diff --git a/luni/src/main/native/android_system_OsConstants.cpp b/luni/src/main/native/android_system_OsConstants.cpp
index 92212b9..a9031f4 100644
--- a/luni/src/main/native/android_system_OsConstants.cpp
+++ b/luni/src/main/native/android_system_OsConstants.cpp
@@ -38,6 +38,9 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <net/if_arp.h>
+#include <linux/if_ether.h>
+
// After the others because these are not necessarily self-contained in glibc.
#ifndef __APPLE__
#include <linux/if_addr.h>
@@ -58,6 +61,8 @@ static void initConstant(JNIEnv* env, jclass c, const char* fieldName, int value
static void OsConstants_initConstants(JNIEnv* env, jclass c) {
initConstant(env, c, "AF_INET", AF_INET);
initConstant(env, c, "AF_INET6", AF_INET6);
+ initConstant(env, c, "AF_PACKET", AF_PACKET);
+ initConstant(env, c, "AF_NETLINK", AF_NETLINK);
initConstant(env, c, "AF_UNIX", AF_UNIX);
initConstant(env, c, "AF_UNSPEC", AF_UNSPEC);
initConstant(env, c, "AI_ADDRCONFIG", AI_ADDRCONFIG);
@@ -69,6 +74,8 @@ static void OsConstants_initConstants(JNIEnv* env, jclass c) {
#endif
initConstant(env, c, "AI_PASSIVE", AI_PASSIVE);
initConstant(env, c, "AI_V4MAPPED", AI_V4MAPPED);
+ initConstant(env, c, "ARPHRD_ETHER", ARPHRD_ETHER);
+ initConstant(env, c, "ARPHRD_LOOPBACK", ARPHRD_LOOPBACK);
#if defined(CAP_LAST_CAP)
initConstant(env, c, "CAP_AUDIT_CONTROL", CAP_AUDIT_CONTROL);
initConstant(env, c, "CAP_AUDIT_WRITE", CAP_AUDIT_WRITE);
@@ -196,6 +203,9 @@ static void OsConstants_initConstants(JNIEnv* env, jclass c) {
initConstant(env, c, "ESPIPE", ESPIPE);
initConstant(env, c, "ESRCH", ESRCH);
initConstant(env, c, "ESTALE", ESTALE);
+ initConstant(env, c, "ETH_P_ARP", ETH_P_ARP);
+ initConstant(env, c, "ETH_P_IP", ETH_P_IP);
+ initConstant(env, c, "ETH_P_IPV6", ETH_P_IPV6);
initConstant(env, c, "ETIME", ETIME);
initConstant(env, c, "ETIMEDOUT", ETIMEDOUT);
initConstant(env, c, "ETXTBSY", ETXTBSY);
@@ -355,6 +365,7 @@ static void OsConstants_initConstants(JNIEnv* env, jclass c) {
initConstant(env, c, "MS_ASYNC", MS_ASYNC);
initConstant(env, c, "MS_INVALIDATE", MS_INVALIDATE);
initConstant(env, c, "MS_SYNC", MS_SYNC);
+ initConstant(env, c, "NETLINK_ROUTE", NETLINK_ROUTE);
initConstant(env, c, "NI_DGRAM", NI_DGRAM);
initConstant(env, c, "NI_NAMEREQD", NI_NAMEREQD);
initConstant(env, c, "NI_NOFQDN", NI_NOFQDN);
@@ -362,6 +373,7 @@ static void OsConstants_initConstants(JNIEnv* env, jclass c) {
initConstant(env, c, "NI_NUMERICSERV", NI_NUMERICSERV);
initConstant(env, c, "O_ACCMODE", O_ACCMODE);
initConstant(env, c, "O_APPEND", O_APPEND);
+ initConstant(env, c, "O_CLOEXEC", O_CLOEXEC);
initConstant(env, c, "O_CREAT", O_CREAT);
initConstant(env, c, "O_EXCL", O_EXCL);
initConstant(env, c, "O_NOCTTY", O_NOCTTY);
@@ -406,6 +418,19 @@ static void OsConstants_initConstants(JNIEnv* env, jclass c) {
initConstant(env, c, "RT_SCOPE_NOWHERE", RT_SCOPE_NOWHERE);
initConstant(env, c, "RT_SCOPE_SITE", RT_SCOPE_SITE);
initConstant(env, c, "RT_SCOPE_UNIVERSE", RT_SCOPE_UNIVERSE);
+ initConstant(env, c, "RTMGRP_IPV4_IFADDR", RTMGRP_IPV4_IFADDR);
+ initConstant(env, c, "RTMGRP_IPV4_MROUTE", RTMGRP_IPV4_MROUTE);
+ initConstant(env, c, "RTMGRP_IPV4_ROUTE", RTMGRP_IPV4_ROUTE);
+ initConstant(env, c, "RTMGRP_IPV4_RULE", RTMGRP_IPV4_RULE);
+ initConstant(env, c, "RTMGRP_IPV6_IFADDR", RTMGRP_IPV6_IFADDR);
+ initConstant(env, c, "RTMGRP_IPV6_IFINFO", RTMGRP_IPV6_IFINFO);
+ initConstant(env, c, "RTMGRP_IPV6_MROUTE", RTMGRP_IPV6_MROUTE);
+ initConstant(env, c, "RTMGRP_IPV6_PREFIX", RTMGRP_IPV6_PREFIX);
+ initConstant(env, c, "RTMGRP_IPV6_ROUTE", RTMGRP_IPV6_ROUTE);
+ initConstant(env, c, "RTMGRP_LINK", RTMGRP_LINK);
+ initConstant(env, c, "RTMGRP_NEIGH", RTMGRP_NEIGH);
+ initConstant(env, c, "RTMGRP_NOTIFY", RTMGRP_NOTIFY);
+ initConstant(env, c, "RTMGRP_TC", RTMGRP_TC);
#endif
initConstant(env, c, "SEEK_CUR", SEEK_CUR);
initConstant(env, c, "SEEK_END", SEEK_END);
@@ -490,6 +515,15 @@ static void OsConstants_initConstants(JNIEnv* env, jclass c) {
initConstant(env, c, "STDERR_FILENO", STDERR_FILENO);
initConstant(env, c, "STDIN_FILENO", STDIN_FILENO);
initConstant(env, c, "STDOUT_FILENO", STDOUT_FILENO);
+ initConstant(env, c, "ST_MANDLOCK", ST_MANDLOCK);
+ initConstant(env, c, "ST_NOATIME", ST_NOATIME);
+ initConstant(env, c, "ST_NODEV", ST_NODEV);
+ initConstant(env, c, "ST_NODIRATIME", ST_NODIRATIME);
+ initConstant(env, c, "ST_NOEXEC", ST_NOEXEC);
+ initConstant(env, c, "ST_NOSUID", ST_NOSUID);
+ initConstant(env, c, "ST_RDONLY", ST_RDONLY);
+ initConstant(env, c, "ST_RELATIME", ST_RELATIME);
+ initConstant(env, c, "ST_SYNCHRONOUS", ST_SYNCHRONOUS);
initConstant(env, c, "S_IFBLK", S_IFBLK);
initConstant(env, c, "S_IFCHR", S_IFCHR);
initConstant(env, c, "S_IFDIR", S_IFDIR);
diff --git a/luni/src/main/native/java_lang_StrictMath.cpp b/luni/src/main/native/java_lang_StrictMath.cpp
index cfe375e..e8c6dfb 100644
--- a/luni/src/main/native/java_lang_StrictMath.cpp
+++ b/luni/src/main/native/java_lang_StrictMath.cpp
@@ -34,26 +34,6 @@ static jdouble StrictMath_tan(JNIEnv*, jclass, jdouble a) {
return ieee_tan(a);
}
-static jdouble StrictMath_asin(JNIEnv*, jclass, jdouble a) {
- return ieee_asin(a);
-}
-
-static jdouble StrictMath_acos(JNIEnv*, jclass, jdouble a) {
- return ieee_acos(a);
-}
-
-static jdouble StrictMath_atan(JNIEnv*, jclass, jdouble a) {
- return ieee_atan(a);
-}
-
-static jdouble StrictMath_exp(JNIEnv*, jclass, jdouble a) {
- return ieee_exp(a);
-}
-
-static jdouble StrictMath_log(JNIEnv*, jclass, jdouble a) {
- return ieee_log(a);
-}
-
static jdouble StrictMath_sqrt(JNIEnv*, jclass, jdouble a) {
return ieee_sqrt(a);
}
@@ -74,75 +54,30 @@ static jdouble StrictMath_rint(JNIEnv*, jclass, jdouble a) {
return ieee_rint(a);
}
-static jdouble StrictMath_atan2(JNIEnv*, jclass, jdouble a, jdouble b) {
- return ieee_atan2(a, b);
-}
-
static jdouble StrictMath_pow(JNIEnv*, jclass, jdouble a, jdouble b) {
return ieee_pow(a,b);
}
-static jdouble StrictMath_sinh(JNIEnv*, jclass, jdouble a) {
- return ieee_sinh(a);
-}
-
-static jdouble StrictMath_tanh(JNIEnv*, jclass, jdouble a) {
- return ieee_tanh(a);
-}
-
-static jdouble StrictMath_cosh(JNIEnv*, jclass, jdouble a) {
- return ieee_cosh(a);
-}
-
-static jdouble StrictMath_log10(JNIEnv*, jclass, jdouble a) {
- return ieee_log10(a);
-}
-
-static jdouble StrictMath_cbrt(JNIEnv*, jclass, jdouble a) {
- return ieee_cbrt(a);
-}
-
-static jdouble StrictMath_expm1(JNIEnv*, jclass, jdouble a) {
- return ieee_expm1(a);
-}
-
static jdouble StrictMath_hypot(JNIEnv*, jclass, jdouble a, jdouble b) {
return ieee_hypot(a, b);
}
-static jdouble StrictMath_log1p(JNIEnv*, jclass, jdouble a) {
- return ieee_log1p(a);
-}
-
static jdouble StrictMath_nextafter(JNIEnv*, jclass, jdouble a, jdouble b) {
return ieee_nextafter(a, b);
}
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(StrictMath, IEEEremainder, "!(DD)D"),
- NATIVE_METHOD(StrictMath, acos, "!(D)D"),
- NATIVE_METHOD(StrictMath, asin, "!(D)D"),
- NATIVE_METHOD(StrictMath, atan, "!(D)D"),
- NATIVE_METHOD(StrictMath, atan2, "!(DD)D"),
- NATIVE_METHOD(StrictMath, cbrt, "!(D)D"),
NATIVE_METHOD(StrictMath, ceil, "!(D)D"),
NATIVE_METHOD(StrictMath, cos, "!(D)D"),
- NATIVE_METHOD(StrictMath, cosh, "!(D)D"),
- NATIVE_METHOD(StrictMath, exp, "!(D)D"),
- NATIVE_METHOD(StrictMath, expm1, "!(D)D"),
NATIVE_METHOD(StrictMath, floor, "!(D)D"),
NATIVE_METHOD(StrictMath, hypot, "!(DD)D"),
- NATIVE_METHOD(StrictMath, log, "!(D)D"),
- NATIVE_METHOD(StrictMath, log10, "!(D)D"),
- NATIVE_METHOD(StrictMath, log1p, "!(D)D"),
NATIVE_METHOD(StrictMath, nextafter, "!(DD)D"),
NATIVE_METHOD(StrictMath, pow, "!(DD)D"),
NATIVE_METHOD(StrictMath, rint, "!(D)D"),
NATIVE_METHOD(StrictMath, sin, "!(D)D"),
- NATIVE_METHOD(StrictMath, sinh, "!(D)D"),
NATIVE_METHOD(StrictMath, sqrt, "!(D)D"),
NATIVE_METHOD(StrictMath, tan, "!(D)D"),
- NATIVE_METHOD(StrictMath, tanh, "!(D)D"),
};
void register_java_lang_StrictMath(JNIEnv* env) {
jniRegisterNativeMethods(env, "java/lang/StrictMath", gMethods, NELEM(gMethods));
diff --git a/luni/src/main/native/java_lang_StringToReal.cpp b/luni/src/main/native/java_lang_StringToReal.cpp
index 108f939..d1902af 100644
--- a/luni/src/main/native/java_lang_StringToReal.cpp
+++ b/luni/src/main/native/java_lang_StringToReal.cpp
@@ -25,9 +25,6 @@
#include "cbigint.h"
/* ************************* Defines ************************* */
-#if defined(__linux__) || defined(__APPLE__)
-#define USE_LL
-#endif
#define LOW_I32_FROM_VAR(u64) LOW_I32_FROM_LONG64(u64)
#define LOW_I32_FROM_PTR(u64ptr) LOW_I32_FROM_LONG64_PTR(u64ptr)
@@ -38,69 +35,13 @@
#define DEFAULT_DOUBLE_WIDTH MAX_DOUBLE_ACCURACY_WIDTH
-#if defined(USE_LL)
#define DOUBLE_INFINITE_LONGBITS (0x7FF0000000000000LL)
-#else
-#if defined(USE_L)
-#define DOUBLE_INFINITE_LONGBITS (0x7FF0000000000000L)
-#else
-#define DOUBLE_INFINITE_LONGBITS (0x7FF0000000000000)
-#endif /* USE_L */
-#endif /* USE_LL */
#define DOUBLE_MINIMUM_LONGBITS (0x1)
-#if defined(USE_LL)
#define DOUBLE_MANTISSA_MASK (0x000FFFFFFFFFFFFFLL)
#define DOUBLE_EXPONENT_MASK (0x7FF0000000000000LL)
#define DOUBLE_NORMAL_MASK (0x0010000000000000LL)
-#else
-#if defined(USE_L)
-#define DOUBLE_MANTISSA_MASK (0x000FFFFFFFFFFFFFL)
-#define DOUBLE_EXPONENT_MASK (0x7FF0000000000000L)
-#define DOUBLE_NORMAL_MASK (0x0010000000000000L)
-#else
-#define DOUBLE_MANTISSA_MASK (0x000FFFFFFFFFFFFF)
-#define DOUBLE_EXPONENT_MASK (0x7FF0000000000000)
-#define DOUBLE_NORMAL_MASK (0x0010000000000000)
-#endif /* USE_L */
-#endif /* USE_LL */
-
-/* Keep a count of the number of times we decrement and increment to
- * approximate the double, and attempt to detect the case where we
- * could potentially toggle back and forth between decrementing and
- * incrementing. It is possible for us to be stuck in the loop when
- * incrementing by one or decrementing by one may exceed or stay below
- * the value that we are looking for. In this case, just break out of
- * the loop if we toggle between incrementing and decrementing for more
- * than twice.
- */
-#define INCREMENT_DOUBLE(_x, _decCount, _incCount) \
- { \
- ++DOUBLE_TO_LONGBITS(_x); \
- _incCount++; \
- if( (_incCount > 2) && (_decCount > 2) ) { \
- if( _decCount > _incCount ) { \
- DOUBLE_TO_LONGBITS(_x) += _decCount - _incCount; \
- } else if( _incCount > _decCount ) { \
- DOUBLE_TO_LONGBITS(_x) -= _incCount - _decCount; \
- } \
- break; \
- } \
- }
-#define DECREMENT_DOUBLE(_x, _decCount, _incCount) \
- { \
- --DOUBLE_TO_LONGBITS(_x); \
- _decCount++; \
- if( (_incCount > 2) && (_decCount > 2) ) { \
- if( _decCount > _incCount ) { \
- DOUBLE_TO_LONGBITS(_x) += _decCount - _incCount; \
- } else if( _incCount > _decCount ) { \
- DOUBLE_TO_LONGBITS(_x) -= _incCount - _decCount; \
- } \
- break; \
- } \
- }
#define allocateU64(x, n) if (!((x) = reinterpret_cast<uint64_t*>(malloc((n) * sizeof(uint64_t))))) goto OutOfMemory;
@@ -248,7 +189,6 @@ static jdouble createDouble(JNIEnv* env, const char* s, jint e) {
}
return result;
-
}
static jdouble createDouble1(JNIEnv* env, uint64_t* f, int32_t length, jint e) {
@@ -310,7 +250,6 @@ static jdouble createDouble1(JNIEnv* env, uint64_t* f, int32_t length, jint e) {
first and let it fall to zero if need be. */
if (result == 0.0)
-
DOUBLE_TO_LONGBITS (result) = DOUBLE_MINIMUM_LONGBITS;
return doubleAlgorithm (env, f, length, e, result);
@@ -323,15 +262,6 @@ static jdouble createDouble1(JNIEnv* env, uint64_t* f, int32_t length, jint e) {
* Clinger, Proceedings of the ACM SIGPLAN '90 Conference on
* Programming Language Design and Implementation, June 20-22,
* 1990, pp. 92-101.
- *
- * There is a possibility that the function will end up in an endless
- * loop if the given approximating floating-point number (a very small
- * floating-point whose value is very close to zero) straddles between
- * two approximating integer values. We modified the algorithm slightly
- * to detect the case where it oscillates back and forth between
- * incrementing and decrementing the floating-point approximation. It
- * is currently set such that if the oscillation occurs more than twice
- * then return the original approximation.
*/
static jdouble doubleAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, jdouble z) {
uint64_t m;
@@ -340,11 +270,10 @@ static jdouble doubleAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e,
uint64_t* y;
uint64_t* D;
uint64_t* D2;
- int32_t xLength, yLength, DLength, D2Length, decApproxCount, incApproxCount;
+ int32_t xLength, yLength, DLength, D2Length;
x = y = D = D2 = 0;
xLength = yLength = DLength = D2Length = 0;
- decApproxCount = incApproxCount = 0;
do
{
@@ -443,12 +372,13 @@ static jdouble doubleAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e,
comparison2 = compareHighPrecision (D2, D2Length, y, yLength);
if (comparison2 < 0)
{
- if (comparison < 0 && m == DOUBLE_NORMAL_MASK)
+ if (comparison < 0 && m == DOUBLE_NORMAL_MASK
+ && DOUBLE_TO_LONGBITS(z) != DOUBLE_NORMAL_MASK)
{
simpleShiftLeftHighPrecision (D2, D2Length, 1);
if (compareHighPrecision (D2, D2Length, y, yLength) > 0)
{
- DECREMENT_DOUBLE (z, decApproxCount, incApproxCount);
+ --DOUBLE_TO_LONGBITS (z);
}
else
{
@@ -466,7 +396,7 @@ static jdouble doubleAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e,
{
if (comparison < 0 && m == DOUBLE_NORMAL_MASK)
{
- DECREMENT_DOUBLE (z, decApproxCount, incApproxCount);
+ --DOUBLE_TO_LONGBITS (z);
}
else
{
@@ -475,24 +405,24 @@ static jdouble doubleAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e,
}
else if (comparison < 0)
{
- DECREMENT_DOUBLE (z, decApproxCount, incApproxCount);
+ --DOUBLE_TO_LONGBITS (z);
break;
}
else
{
- INCREMENT_DOUBLE (z, decApproxCount, incApproxCount);
+ ++DOUBLE_TO_LONGBITS (z);
break;
}
}
else if (comparison < 0)
{
- DECREMENT_DOUBLE (z, decApproxCount, incApproxCount);
+ --DOUBLE_TO_LONGBITS (z);
}
else
{
if (DOUBLE_TO_LONGBITS (z) == DOUBLE_INFINITE_LONGBITS)
break;
- INCREMENT_DOUBLE (z, decApproxCount, incApproxCount);
+ ++DOUBLE_TO_LONGBITS (z);
}
}
while (1);
@@ -547,42 +477,6 @@ static const uint32_t float_tens[] = {
#define FLOAT_EXPONENT_MASK (0x7F800000)
#define FLOAT_NORMAL_MASK (0x00800000)
-/* Keep a count of the number of times we decrement and increment to
- * approximate the double, and attempt to detect the case where we
- * could potentially toggle back and forth between decrementing and
- * incrementing. It is possible for us to be stuck in the loop when
- * incrementing by one or decrementing by one may exceed or stay below
- * the value that we are looking for. In this case, just break out of
- * the loop if we toggle between incrementing and decrementing for more
- * than twice.
- */
-#define INCREMENT_FLOAT(_x, _decCount, _incCount) \
- { \
- ++FLOAT_TO_INTBITS(_x); \
- _incCount++; \
- if( (_incCount > 2) && (_decCount > 2) ) { \
- if( _decCount > _incCount ) { \
- FLOAT_TO_INTBITS(_x) += _decCount - _incCount; \
- } else if( _incCount > _decCount ) { \
- FLOAT_TO_INTBITS(_x) -= _incCount - _decCount; \
- } \
- break; \
- } \
- }
-#define DECREMENT_FLOAT(_x, _decCount, _incCount) \
- { \
- --FLOAT_TO_INTBITS(_x); \
- _decCount++; \
- if( (_incCount > 2) && (_decCount > 2) ) { \
- if( _decCount > _incCount ) { \
- FLOAT_TO_INTBITS(_x) += _decCount - _incCount; \
- } else if( _incCount > _decCount ) { \
- FLOAT_TO_INTBITS(_x) -= _incCount - _decCount; \
- } \
- break; \
- } \
- }
-
static jfloat createFloat(JNIEnv* env, const char* s, jint e) {
/* assumes s is a null terminated string with at least one
* character in it */
@@ -682,7 +576,6 @@ static jfloat createFloat(JNIEnv* env, const char* s, jint e) {
}
return result;
-
}
static jfloat createFloat1 (JNIEnv* env, uint64_t* f, int32_t length, jint e) {
@@ -796,15 +689,6 @@ static jfloat createFloat1 (JNIEnv* env, uint64_t* f, int32_t length, jint e) {
* Clinger, Proceedings of the ACM SIGPLAN '90 Conference on
* Programming Language Design and Implementation, June 20-22,
* 1990, pp. 92-101.
- *
- * There is a possibility that the function will end up in an endless
- * loop if the given approximating floating-point number (a very small
- * floating-point whose value is very close to zero) straddles between
- * two approximating integer values. We modified the algorithm slightly
- * to detect the case where it oscillates back and forth between
- * incrementing and decrementing the floating-point approximation. It
- * is currently set such that if the oscillation occurs more than twice
- * then return the original approximation.
*/
static jfloat floatAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, jfloat z) {
uint64_t m;
@@ -814,11 +698,9 @@ static jfloat floatAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, j
uint64_t* D;
uint64_t* D2;
int32_t xLength, yLength, DLength, D2Length;
- int32_t decApproxCount, incApproxCount;
x = y = D = D2 = 0;
xLength = yLength = DLength = D2Length = 0;
- decApproxCount = incApproxCount = 0;
do
{
@@ -917,12 +799,13 @@ static jfloat floatAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, j
comparison2 = compareHighPrecision (D2, D2Length, y, yLength);
if (comparison2 < 0)
{
- if (comparison < 0 && m == FLOAT_NORMAL_MASK)
+ if (comparison < 0 && m == FLOAT_NORMAL_MASK
+ && FLOAT_TO_INTBITS(z) != FLOAT_NORMAL_MASK)
{
simpleShiftLeftHighPrecision (D2, D2Length, 1);
if (compareHighPrecision (D2, D2Length, y, yLength) > 0)
{
- DECREMENT_FLOAT (z, decApproxCount, incApproxCount);
+ --FLOAT_TO_INTBITS (z);
}
else
{
@@ -940,7 +823,7 @@ static jfloat floatAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, j
{
if (comparison < 0 && m == FLOAT_NORMAL_MASK)
{
- DECREMENT_FLOAT (z, decApproxCount, incApproxCount);
+ --FLOAT_TO_INTBITS (z);
}
else
{
@@ -949,24 +832,24 @@ static jfloat floatAlgorithm(JNIEnv* env, uint64_t* f, int32_t length, jint e, j
}
else if (comparison < 0)
{
- DECREMENT_FLOAT (z, decApproxCount, incApproxCount);
+ --FLOAT_TO_INTBITS (z);
break;
}
else
{
- INCREMENT_FLOAT (z, decApproxCount, incApproxCount);
+ ++FLOAT_TO_INTBITS (z);
break;
}
}
else if (comparison < 0)
{
- DECREMENT_FLOAT (z, decApproxCount, incApproxCount);
+ --FLOAT_TO_INTBITS (z);
}
else
{
if (FLOAT_TO_INTBITS (z) == FLOAT_EXPONENT_MASK)
break;
- INCREMENT_FLOAT (z, decApproxCount, incApproxCount);
+ ++FLOAT_TO_INTBITS (z);
}
}
while (1);
diff --git a/luni/src/main/native/java_lang_System.cpp b/luni/src/main/native/java_lang_System.cpp
index 944c0c3..306adab 100644
--- a/luni/src/main/native/java_lang_System.cpp
+++ b/luni/src/main/native/java_lang_System.cpp
@@ -84,7 +84,11 @@ static jobjectArray System_specialProperties(JNIEnv* env, jclass) {
properties.push_back(std::string("user.dir=") + getcwd(path, sizeof(path)));
properties.push_back("android.zlib.version=" ZLIB_VERSION);
+#if defined(OPENSSL_IS_BORINGSSL)
+ properties.push_back("android.openssl.version=BoringSSL");
+#else
properties.push_back("android.openssl.version=" OPENSSL_VERSION_TEXT);
+#endif
const char* library_path = getenv("LD_LIBRARY_PATH");
#if defined(HAVE_ANDROID_OS)
@@ -109,33 +113,20 @@ static jlong System_currentTimeMillis(JNIEnv*, jclass) {
}
static jlong System_nanoTime(JNIEnv*, jclass) {
-#if defined(HAVE_POSIX_CLOCKS)
+#if defined(__linux__)
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return now.tv_sec * 1000000000LL + now.tv_nsec;
-#else
+#else // __APPLE__
timeval now;
gettimeofday(&now, NULL);
return static_cast<jlong>(now.tv_sec) * 1000000000LL + now.tv_usec * 1000LL;
#endif
}
-static jstring System_mapLibraryName(JNIEnv* env, jclass, jstring javaName) {
- ScopedUtfChars name(env, javaName);
- if (name.c_str() == NULL) {
- return NULL;
- }
- char* mappedName = NULL;
- asprintf(&mappedName, OS_SHARED_LIB_FORMAT_STR, name.c_str());
- jstring result = env->NewStringUTF(mappedName);
- free(mappedName);
- return result;
-}
-
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(System, currentTimeMillis, "!()J"),
NATIVE_METHOD(System, log, "(CLjava/lang/String;Ljava/lang/Throwable;)V"),
- NATIVE_METHOD(System, mapLibraryName, "(Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(System, nanoTime, "!()J"),
NATIVE_METHOD(System, setFieldImpl, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V"),
NATIVE_METHOD(System, specialProperties, "()[Ljava/lang/String;"),
diff --git a/luni/src/main/native/java_math_NativeBN.cpp b/luni/src/main/native/java_math_NativeBN.cpp
index be87ea6..7e77e23 100644
--- a/luni/src/main/native/java_math_NativeBN.cpp
+++ b/luni/src/main/native/java_math_NativeBN.cpp
@@ -21,19 +21,31 @@
#include "JniException.h"
#include "ScopedPrimitiveArray.h"
#include "ScopedUtfChars.h"
-#include "UniquePtr.h"
#include "jni.h"
#include <openssl/bn.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <stdio.h>
+#include <memory>
+
+#if defined(OPENSSL_IS_BORINGSSL)
+/* BoringSSL no longer exports |bn_check_top|. */
+static void bn_check_top(const BIGNUM* bn) {
+ /* This asserts that |bn->top| (which contains the number of elements of
+ * |bn->d| that are valid) is minimal. In other words, that there aren't
+ * superfluous zeros. */
+ if (bn != NULL && bn->top != 0 && bn->d[bn->top-1] == 0) {
+ abort();
+ }
+}
+#endif
struct BN_CTX_Deleter {
void operator()(BN_CTX* p) const {
BN_CTX_free(p);
}
};
-typedef UniquePtr<BN_CTX, BN_CTX_Deleter> Unique_BN_CTX;
+typedef std::unique_ptr<BN_CTX, BN_CTX_Deleter> Unique_BN_CTX;
static BIGNUM* toBigNum(jlong address) {
return reinterpret_cast<BIGNUM*>(static_cast<uintptr_t>(address));
@@ -109,28 +121,32 @@ static void NativeBN_BN_copy(JNIEnv* env, jclass, jlong to, jlong from) {
}
static void NativeBN_putULongInt(JNIEnv* env, jclass, jlong a0, jlong java_dw, jboolean neg) {
- if (!oneValidHandle(env, a0)) return;
+ if (!oneValidHandle(env, a0)) return;
- uint64_t dw = java_dw;
+ uint64_t dw = java_dw;
+ BIGNUM* a = toBigNum(a0);
+ int ok;
- // cf. litEndInts2bn:
- BIGNUM* a = toBigNum(a0);
- bn_check_top(a);
- if (bn_wexpand(a, 8/BN_BYTES) != NULL) {
-#ifdef __LP64__
+ static_assert(sizeof(dw) == sizeof(BN_ULONG) ||
+ sizeof(dw) == 2*sizeof(BN_ULONG), "Unknown BN configuration");
+
+ if (sizeof(dw) == sizeof(BN_ULONG)) {
+ ok = BN_set_word(a, dw);
+ } else if (sizeof(dw) == 2 * sizeof(BN_ULONG)) {
+ ok = (bn_wexpand(a, 2) != NULL);
+ if (ok) {
a->d[0] = dw;
-#else
- unsigned int hi = dw >> 32; // This shifts without sign extension.
- int lo = (int)dw; // This truncates implicitly.
- a->d[0] = lo;
- a->d[1] = hi;
-#endif
- a->top = 8 / BN_BYTES;
- a->neg = neg;
+ a->d[1] = dw >> 32;
+ a->top = 2;
bn_correct_top(a);
- } else {
- throwExceptionIfNecessary(env);
}
+ }
+
+ BN_set_negative(a, neg);
+
+ if (!ok) {
+ throwExceptionIfNecessary(env);
+ }
}
static void NativeBN_putLongInt(JNIEnv* env, jclass cls, jlong a, jlong dw) {
@@ -240,25 +256,25 @@ static void negBigEndianBytes2bn(JNIEnv*, jclass, const unsigned char* bytes, in
bn_check_top(ret);
// FIXME: assert bytesLen > 0
- int wLen = (bytesLen + BN_BYTES - 1) / BN_BYTES;
+ int wLen = (bytesLen + sizeof(BN_ULONG) - 1) / sizeof(BN_ULONG);
int firstNonzeroDigit = -2;
if (bn_wexpand(ret, wLen) != NULL) {
BN_ULONG* d = ret->d;
BN_ULONG di;
ret->top = wLen;
- int highBytes = bytesLen % BN_BYTES;
+ int highBytes = bytesLen % sizeof(BN_ULONG);
int k = bytesLen;
// Put bytes to the int array starting from the end of the byte array
int i = 0;
while (k > highBytes) {
- k -= BN_BYTES;
+ k -= sizeof(BN_ULONG);
di = BYTES2ULONG(bytes, k);
if (di != 0) {
d[i] = -di;
firstNonzeroDigit = i;
i++;
while (k > highBytes) {
- k -= BN_BYTES;
+ k -= sizeof(BN_ULONG);
d[i] = ~BYTES2ULONG(bytes, k);
i++;
}
@@ -394,7 +410,7 @@ static jintArray NativeBN_bn2litEndInts(JNIEnv* env, jclass, jlong a0) {
if (wLen == 0) {
return NULL;
}
- jintArray result = env->NewIntArray(wLen * BN_BYTES/sizeof(unsigned int));
+ jintArray result = env->NewIntArray(wLen * sizeof(BN_ULONG)/sizeof(unsigned int));
if (result == NULL) {
return NULL;
}
@@ -445,7 +461,7 @@ static int NativeBN_bitLength(JNIEnv* env, jclass, jlong a0) {
do { i--; } while (!((i < 0) || (d[i] != 0)));
if (i < 0) msd--; // Only if all lower significant digits are 0 we decrement the most significant one.
}
- return (wLen - 1) * BN_BYTES * 8 + BN_num_bits_word(msd);
+ return (wLen - 1) * sizeof(BN_ULONG) * 8 + BN_num_bits_word(msd);
}
static jboolean NativeBN_BN_is_bit_set(JNIEnv* env, jclass, jlong a, int n) {
diff --git a/luni/src/main/native/java_text_Bidi.cpp b/luni/src/main/native/java_text_Bidi.cpp
index d9ef35d..6a3e751 100644
--- a/luni/src/main/native/java_text_Bidi.cpp
+++ b/luni/src/main/native/java_text_Bidi.cpp
@@ -22,14 +22,14 @@
#include "JniConstants.h"
#include "JniException.h"
#include "ScopedPrimitiveArray.h"
-#include "UniquePtr.h"
#include "unicode/ubidi.h"
#include <stdlib.h>
#include <string.h>
+#include <memory>
struct BiDiData {
- BiDiData(UBiDi* biDi) : mBiDi(biDi), mEmbeddingLevels(NULL) {
+ BiDiData(UBiDi* biDi) : mBiDi(biDi) {
}
~BiDiData() {
@@ -50,7 +50,7 @@ struct BiDiData {
private:
UBiDi* mBiDi;
- UniquePtr<jbyte[]> mEmbeddingLevels;
+ std::unique_ptr<jbyte[]> mEmbeddingLevels;
// Disallow copy and assignment.
BiDiData(const BiDiData&);
@@ -98,7 +98,7 @@ static jlong Bidi_ubidi_setLine(JNIEnv* env, jclass, jlong ptr, jint start, jint
if (maybeThrowIcuException(env, "ubidi_openSized", status)) {
return 0;
}
- UniquePtr<BiDiData> lineData(new BiDiData(sized));
+ std::unique_ptr<BiDiData> lineData(new BiDiData(sized));
ubidi_setLine(uBiDi(ptr), start, limit, lineData->uBiDi(), &status);
maybeThrowIcuException(env, "ubidi_setLine", status);
return reinterpret_cast<uintptr_t>(lineData.release());
@@ -168,7 +168,7 @@ static jintArray Bidi_ubidi_reorderVisual(JNIEnv* env, jclass, jbyteArray javaLe
const UBiDiLevel* levels = reinterpret_cast<const UBiDiLevel*>(levelBytes.get());
- UniquePtr<int[]> indexMap(new int[length]);
+ std::unique_ptr<int[]> indexMap(new int[length]);
ubidi_reorderVisual(levels, length, &indexMap[0]);
jintArray result = env->NewIntArray(length);
diff --git a/luni/src/main/native/java_util_jar_StrictJarFile.cpp b/luni/src/main/native/java_util_jar_StrictJarFile.cpp
index efcc74c..82547bd 100644
--- a/luni/src/main/native/java_util_jar_StrictJarFile.cpp
+++ b/luni/src/main/native/java_util_jar_StrictJarFile.cpp
@@ -17,49 +17,26 @@
#define LOG_TAG "StrictJarFile"
+#include <memory>
#include <string>
#include "JNIHelp.h"
#include "JniConstants.h"
#include "ScopedLocalRef.h"
#include "ScopedUtfChars.h"
-#include "UniquePtr.h"
#include "jni.h"
#include "ziparchive/zip_archive.h"
#include "cutils/log.h"
+// The method ID for ZipEntry.<init>(String,String,JJJIII[BJJ)
+static jmethodID zipEntryCtor;
+
static void throwIoException(JNIEnv* env, const int32_t errorCode) {
jniThrowException(env, "java/io/IOException", ErrorCodeString(errorCode));
}
-// Constructs a string out of |name| with the default charset (UTF-8 on android).
-// We prefer this to JNI's NewStringUTF because the string constructor will
-// replace unmappable and malformed bytes instead of throwing. See b/18584205
-//
-// Returns |NULL| iff. we couldn't allocate the string object or its constructor
-// arguments.
-//
-// TODO: switch back to NewStringUTF after libziparchive is modified to reject
-// files whose names aren't valid UTF-8.
-static jobject constructString(JNIEnv* env, const char* name, const uint16_t nameLength) {
- jbyteArray javaNameBytes = env->NewByteArray(nameLength);
- if (javaNameBytes == NULL) {
- return NULL;
- }
- env->SetByteArrayRegion(javaNameBytes, 0, nameLength, reinterpret_cast<const jbyte*>(name));
-
- ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String"));
- const jmethodID stringCtor = env->GetMethodID(stringClass.get(), "<init>", "([B)V");
- return env->NewObject(stringClass.get(), stringCtor, javaNameBytes);
-}
-
-static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, const jobject entryName,
- const uint16_t nameLength) {
- ScopedLocalRef<jclass> zipEntryClass(env, env->FindClass("java/util/zip/ZipEntry"));
- const jmethodID zipEntryCtor = env->GetMethodID(zipEntryClass.get(), "<init>",
- "(Ljava/lang/String;Ljava/lang/String;JJJIII[BIJJ)V");
-
- return env->NewObject(zipEntryClass.get(),
+static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, jstring entryName) {
+ return env->NewObject(JniConstants::zipEntryClass,
zipEntryCtor,
entryName,
NULL, // comment
@@ -70,16 +47,10 @@ static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, const jobject ent
static_cast<jint>(0), // time
static_cast<jint>(0), // modData
NULL, // byte[] extra
- static_cast<jint>(nameLength),
static_cast<jlong>(-1), // local header offset
static_cast<jlong>(entry.offset));
}
-static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, const char* name,
- const uint16_t nameLength) {
- return newZipEntry(env, entry, constructString(env, name, nameLength), nameLength);
-}
-
static jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring fileName) {
ScopedUtfChars fileChars(env, fileName);
if (fileChars.c_str() == NULL) {
@@ -98,25 +69,20 @@ static jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring fileN
class IterationHandle {
public:
- IterationHandle(const char* prefix) :
- cookie_(NULL), prefix_(strdup(prefix)) {
+ IterationHandle() :
+ cookie_(NULL) {
}
void** CookieAddress() {
return &cookie_;
}
- const char* Prefix() const {
- return prefix_;
- }
-
~IterationHandle() {
- free(prefix_);
+ EndIteration(cookie_);
}
private:
void* cookie_;
- char* prefix_;
};
@@ -127,14 +93,15 @@ static jlong StrictJarFile_nativeStartIteration(JNIEnv* env, jobject, jlong nati
return static_cast<jlong>(-1);
}
- IterationHandle* handle = new IterationHandle(prefixChars.c_str());
+ IterationHandle* handle = new IterationHandle();
int32_t error = 0;
if (prefixChars.size() == 0) {
error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
handle->CookieAddress(), NULL);
} else {
+ ZipEntryName entry_name(prefixChars.c_str());
error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
- handle->CookieAddress(), handle->Prefix());
+ handle->CookieAddress(), &entry_name);
}
if (error) {
@@ -156,11 +123,12 @@ static jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterati
return NULL;
}
- UniquePtr<char[]> entryNameCString(new char[entryName.name_length + 1]);
+ std::unique_ptr<char[]> entryNameCString(new char[entryName.name_length + 1]);
memcpy(entryNameCString.get(), entryName.name, entryName.name_length);
entryNameCString[entryName.name_length] = '\0';
+ ScopedLocalRef<jstring> entryNameString(env, env->NewStringUTF(entryNameCString.get()));
- return newZipEntry(env, data, entryNameCString.get(), entryName.name_length);
+ return newZipEntry(env, data, entryNameString.get());
}
static jobject StrictJarFile_nativeFindEntry(JNIEnv* env, jobject, jlong nativeHandle,
@@ -172,12 +140,12 @@ static jobject StrictJarFile_nativeFindEntry(JNIEnv* env, jobject, jlong nativeH
ZipEntry data;
const int32_t error = FindEntry(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
- entryNameChars.c_str(), &data);
+ ZipEntryName(entryNameChars.c_str()), &data);
if (error) {
return NULL;
}
- return newZipEntry(env, data, entryName, entryNameChars.size());
+ return newZipEntry(env, data, entryName);
}
static void StrictJarFile_nativeClose(JNIEnv*, jobject, jlong nativeHandle) {
@@ -194,4 +162,8 @@ static JNINativeMethod gMethods[] = {
void register_java_util_jar_StrictJarFile(JNIEnv* env) {
jniRegisterNativeMethods(env, "java/util/jar/StrictJarFile", gMethods, NELEM(gMethods));
+
+ zipEntryCtor = env->GetMethodID(JniConstants::zipEntryClass, "<init>",
+ "(Ljava/lang/String;Ljava/lang/String;JJJIII[BJJ)V");
+ LOG_ALWAYS_FATAL_IF(zipEntryCtor == NULL, "Unable to find ZipEntry.<init>");
}
diff --git a/luni/src/main/native/java_util_regex_Matcher.cpp b/luni/src/main/native/java_util_regex_Matcher.cpp
index 2e5259e..35d014c 100644
--- a/luni/src/main/native/java_util_regex_Matcher.cpp
+++ b/luni/src/main/native/java_util_regex_Matcher.cpp
@@ -23,15 +23,14 @@
#include "JniConstants.h"
#include "JniException.h"
#include "ScopedPrimitiveArray.h"
-#include "UniquePtr.h"
#include "jni.h"
#include "unicode/parseerr.h"
#include "unicode/regex.h"
// ICU documentation: http://icu-project.org/apiref/icu4c/classRegexMatcher.html
-static RegexMatcher* toRegexMatcher(jlong address) {
- return reinterpret_cast<RegexMatcher*>(static_cast<uintptr_t>(address));
+static icu::RegexMatcher* toRegexMatcher(jlong address) {
+ return reinterpret_cast<icu::RegexMatcher*>(static_cast<uintptr_t>(address));
}
/**
@@ -75,7 +74,7 @@ public:
maybeThrowIcuException(mEnv, "utext_close", mStatus);
}
- RegexMatcher* operator->() {
+ icu::RegexMatcher* operator->() {
return mMatcher;
}
@@ -107,7 +106,7 @@ private:
JNIEnv* mEnv;
jstring mJavaInput;
- RegexMatcher* mMatcher;
+ icu::RegexMatcher* mMatcher;
const jchar* mChars;
UErrorCode mStatus;
UText* mUText;
@@ -171,9 +170,9 @@ static jint Matcher_matchesImpl(JNIEnv* env, jclass, jlong addr, jstring javaTex
}
static jlong Matcher_openImpl(JNIEnv* env, jclass, jlong patternAddr) {
- RegexPattern* pattern = reinterpret_cast<RegexPattern*>(static_cast<uintptr_t>(patternAddr));
+ icu::RegexPattern* pattern = reinterpret_cast<icu::RegexPattern*>(static_cast<uintptr_t>(patternAddr));
UErrorCode status = U_ZERO_ERROR;
- RegexMatcher* result = pattern->matcher(status);
+ icu::RegexMatcher* result = pattern->matcher(status);
maybeThrowIcuException(env, "RegexPattern::matcher", status);
return reinterpret_cast<uintptr_t>(result);
}
diff --git a/luni/src/main/native/java_util_regex_Pattern.cpp b/luni/src/main/native/java_util_regex_Pattern.cpp
index 1a99d0a..f2c07dc 100644
--- a/luni/src/main/native/java_util_regex_Pattern.cpp
+++ b/luni/src/main/native/java_util_regex_Pattern.cpp
@@ -27,8 +27,8 @@
// ICU documentation: http://icu-project.org/apiref/icu4c/classRegexPattern.html
-static RegexPattern* toRegexPattern(jlong addr) {
- return reinterpret_cast<RegexPattern*>(static_cast<uintptr_t>(addr));
+static icu::RegexPattern* toRegexPattern(jlong addr) {
+ return reinterpret_cast<icu::RegexPattern*>(static_cast<uintptr_t>(addr));
}
static const char* regexDetailMessage(UErrorCode status) {
@@ -86,8 +86,8 @@ static jlong Pattern_compileImpl(JNIEnv* env, jclass, jstring javaRegex, jint fl
if (!regex.valid()) {
return 0;
}
- UnicodeString& regexString(regex.unicodeString());
- RegexPattern* result = RegexPattern::compile(regexString, flags, error, status);
+ icu::UnicodeString& regexString(regex.unicodeString());
+ icu::RegexPattern* result = icu::RegexPattern::compile(regexString, flags, error, status);
if (!U_SUCCESS(status)) {
throwPatternSyntaxException(env, status, javaRegex, error);
}
diff --git a/luni/src/main/native/java_util_zip_Deflater.cpp b/luni/src/main/native/java_util_zip_Deflater.cpp
index 1afd36e..d963824 100644
--- a/luni/src/main/native/java_util_zip_Deflater.cpp
+++ b/luni/src/main/native/java_util_zip_Deflater.cpp
@@ -28,11 +28,11 @@ static void Deflater_setDictionaryImpl(JNIEnv* env, jobject, jbyteArray dict, in
}
static jlong Deflater_getTotalInImpl(JNIEnv*, jobject, jlong handle) {
- return toNativeZipStream(handle)->stream.total_in;
+ return toNativeZipStream(handle)->totalIn;
}
static jlong Deflater_getTotalOutImpl(JNIEnv*, jobject, jlong handle) {
- return toNativeZipStream(handle)->stream.total_out;
+ return toNativeZipStream(handle)->totalOut;
}
static jint Deflater_getAdlerImpl(JNIEnv*, jobject, jlong handle) {
@@ -40,7 +40,7 @@ static jint Deflater_getAdlerImpl(JNIEnv*, jobject, jlong handle) {
}
static jlong Deflater_createStream(JNIEnv * env, jobject, jint level, jint strategy, jboolean noHeader) {
- UniquePtr<NativeZipStream> jstream(new NativeZipStream);
+ std::unique_ptr<NativeZipStream> jstream(new NativeZipStream);
if (jstream.get() == NULL) {
jniThrowOutOfMemoryError(env, NULL);
return -1;
@@ -101,6 +101,9 @@ static jint Deflater_deflateImpl(JNIEnv* env, jobject recv, jbyteArray buf, int
jint bytesRead = stream->stream.next_in - initialNextIn;
jint bytesWritten = stream->stream.next_out - initialNextOut;
+ stream->totalIn += bytesRead;
+ stream->totalOut += bytesWritten;
+
static jfieldID inReadField = env->GetFieldID(JniConstants::deflaterClass, "inRead", "I");
jint inReadValue = env->GetIntField(recv, inReadField);
inReadValue += bytesRead;
@@ -116,6 +119,8 @@ static void Deflater_endImpl(JNIEnv*, jobject, jlong handle) {
static void Deflater_resetImpl(JNIEnv* env, jobject, jlong handle) {
NativeZipStream* stream = toNativeZipStream(handle);
+ stream->totalIn = 0;
+ stream->totalOut = 0;
int err = deflateReset(&stream->stream);
if (err != Z_OK) {
throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err, stream);
diff --git a/luni/src/main/native/java_util_zip_Inflater.cpp b/luni/src/main/native/java_util_zip_Inflater.cpp
index ca3ee09..f0878ff 100644
--- a/luni/src/main/native/java_util_zip_Inflater.cpp
+++ b/luni/src/main/native/java_util_zip_Inflater.cpp
@@ -25,7 +25,7 @@
#include <errno.h>
static jlong Inflater_createStream(JNIEnv* env, jobject, jboolean noHeader) {
- UniquePtr<NativeZipStream> jstream(new NativeZipStream);
+ std::unique_ptr<NativeZipStream> jstream(new NativeZipStream);
if (jstream.get() == NULL) {
jniThrowOutOfMemoryError(env, NULL);
return -1;
@@ -122,6 +122,9 @@ static jint Inflater_inflateImpl(JNIEnv* env, jobject recv, jbyteArray buf, int
jint bytesRead = stream->stream.next_in - initialNextIn;
jint bytesWritten = stream->stream.next_out - initialNextOut;
+ stream->totalIn += bytesRead;
+ stream->totalOut += bytesWritten;
+
static jfieldID inReadField = env->GetFieldID(JniConstants::inflaterClass, "inRead", "I");
jint inReadValue = env->GetIntField(recv, inReadField);
inReadValue += bytesRead;
@@ -145,6 +148,8 @@ static void Inflater_setDictionaryImpl(JNIEnv* env, jobject, jbyteArray dict, in
static void Inflater_resetImpl(JNIEnv* env, jobject, jlong handle) {
NativeZipStream* stream = toNativeZipStream(handle);
+ stream->totalIn = 0;
+ stream->totalOut = 0;
int err = inflateReset(&stream->stream);
if (err != Z_OK) {
throwExceptionForZlibError(env, "java/lang/IllegalArgumentException", err, stream);
@@ -152,11 +157,11 @@ static void Inflater_resetImpl(JNIEnv* env, jobject, jlong handle) {
}
static jlong Inflater_getTotalOutImpl(JNIEnv*, jobject, jlong handle) {
- return toNativeZipStream(handle)->stream.total_out;
+ return toNativeZipStream(handle)->totalOut;
}
static jlong Inflater_getTotalInImpl(JNIEnv*, jobject, jlong handle) {
- return toNativeZipStream(handle)->stream.total_in;
+ return toNativeZipStream(handle)->totalIn;
}
static JNINativeMethod gMethods[] = {
diff --git a/luni/src/main/native/libcore_icu_AlphabeticIndex.cpp b/luni/src/main/native/libcore_icu_AlphabeticIndex.cpp
index e0638bd..acc247b 100644
--- a/luni/src/main/native/libcore_icu_AlphabeticIndex.cpp
+++ b/luni/src/main/native/libcore_icu_AlphabeticIndex.cpp
@@ -25,8 +25,8 @@
#include "unicode/alphaindex.h"
#include "unicode/uniset.h"
-static AlphabeticIndex* fromPeer(jlong peer) {
- return reinterpret_cast<AlphabeticIndex*>(static_cast<uintptr_t>(peer));
+static icu::AlphabeticIndex* fromPeer(jlong peer) {
+ return reinterpret_cast<icu::AlphabeticIndex*>(static_cast<uintptr_t>(peer));
}
static jlong AlphabeticIndex_create(JNIEnv* env, jclass, jstring javaLocaleName) {
@@ -35,7 +35,7 @@ static jlong AlphabeticIndex_create(JNIEnv* env, jclass, jstring javaLocaleName)
if (!icuLocale.valid()) {
return 0;
}
- AlphabeticIndex* ai = new AlphabeticIndex(icuLocale.locale(), status);
+ icu::AlphabeticIndex* ai = new icu::AlphabeticIndex(icuLocale.locale(), status);
if (maybeThrowIcuException(env, "AlphabeticIndex", status)) {
return 0;
}
@@ -47,19 +47,19 @@ static void AlphabeticIndex_destroy(JNIEnv*, jclass, jlong peer) {
}
static jint AlphabeticIndex_getMaxLabelCount(JNIEnv*, jclass, jlong peer) {
- AlphabeticIndex* ai = fromPeer(peer);
+ icu::AlphabeticIndex* ai = fromPeer(peer);
return ai->getMaxLabelCount();
}
static void AlphabeticIndex_setMaxLabelCount(JNIEnv* env, jclass, jlong peer, jint count) {
- AlphabeticIndex* ai = fromPeer(peer);
+ icu::AlphabeticIndex* ai = fromPeer(peer);
UErrorCode status = U_ZERO_ERROR;
ai->setMaxLabelCount(count, status);
maybeThrowIcuException(env, "AlphabeticIndex::setMaxLabelCount", status);
}
static void AlphabeticIndex_addLabels(JNIEnv* env, jclass, jlong peer, jstring javaLocaleName) {
- AlphabeticIndex* ai = fromPeer(peer);
+ icu::AlphabeticIndex* ai = fromPeer(peer);
ScopedIcuLocale icuLocale(env, javaLocaleName);
if (!icuLocale.valid()) {
return;
@@ -71,14 +71,14 @@ static void AlphabeticIndex_addLabels(JNIEnv* env, jclass, jlong peer, jstring j
static void AlphabeticIndex_addLabelRange(JNIEnv* env, jclass, jlong peer,
jint codePointStart, jint codePointEnd) {
- AlphabeticIndex* ai = fromPeer(peer);
+ icu::AlphabeticIndex* ai = fromPeer(peer);
UErrorCode status = U_ZERO_ERROR;
- ai->addLabels(UnicodeSet(codePointStart, codePointEnd), status);
+ ai->addLabels(icu::UnicodeSet(codePointStart, codePointEnd), status);
maybeThrowIcuException(env, "AlphabeticIndex::addLabels", status);
}
static jint AlphabeticIndex_getBucketCount(JNIEnv* env, jclass, jlong peer) {
- AlphabeticIndex* ai = fromPeer(peer);
+ icu::AlphabeticIndex* ai = fromPeer(peer);
UErrorCode status = U_ZERO_ERROR;
jint result = ai->getBucketCount(status);
if (maybeThrowIcuException(env, "AlphabeticIndex::getBucketCount", status)) {
@@ -88,7 +88,7 @@ static jint AlphabeticIndex_getBucketCount(JNIEnv* env, jclass, jlong peer) {
}
static jint AlphabeticIndex_getBucketIndex(JNIEnv* env, jclass, jlong peer, jstring javaString) {
- AlphabeticIndex* ai = fromPeer(peer);
+ icu::AlphabeticIndex* ai = fromPeer(peer);
ScopedJavaUnicodeString string(env, javaString);
if (!string.valid()) {
return -1;
@@ -108,7 +108,7 @@ static jstring AlphabeticIndex_getBucketLabel(JNIEnv* env, jclass, jlong peer, j
}
// Iterate to the nth bucket.
- AlphabeticIndex* ai = fromPeer(peer);
+ icu::AlphabeticIndex* ai = fromPeer(peer);
UErrorCode status = U_ZERO_ERROR;
ai->resetBucketIterator(status);
if (maybeThrowIcuException(env, "AlphabeticIndex::resetBucketIterator", status)) {
@@ -129,31 +129,31 @@ static jstring AlphabeticIndex_getBucketLabel(JNIEnv* env, jclass, jlong peer, j
return env->NewStringUTF("");
}
- const UnicodeString& label(ai->getBucketLabel());
+ const icu::UnicodeString& label(ai->getBucketLabel());
return env->NewString(label.getBuffer(), label.length());
}
static jlong AlphabeticIndex_buildImmutableIndex(JNIEnv* env, jclass, jlong peer) {
- AlphabeticIndex* ai = fromPeer(peer);
+ icu::AlphabeticIndex* ai = fromPeer(peer);
UErrorCode status = U_ZERO_ERROR;
- AlphabeticIndex::ImmutableIndex* ii = ai->buildImmutableIndex(status);
+ icu::AlphabeticIndex::ImmutableIndex* ii = ai->buildImmutableIndex(status);
if (maybeThrowIcuException(env, "AlphabeticIndex::buildImmutableIndex", status)) {
return 0;
}
return reinterpret_cast<uintptr_t>(ii);
}
-static AlphabeticIndex::ImmutableIndex* immutableIndexFromPeer(jlong peer) {
- return reinterpret_cast<AlphabeticIndex::ImmutableIndex*>(static_cast<uintptr_t>(peer));
+static icu::AlphabeticIndex::ImmutableIndex* immutableIndexFromPeer(jlong peer) {
+ return reinterpret_cast<icu::AlphabeticIndex::ImmutableIndex*>(static_cast<uintptr_t>(peer));
}
static jint ImmutableIndex_getBucketCount(JNIEnv*, jclass, jlong peer) {
- AlphabeticIndex::ImmutableIndex* ii = immutableIndexFromPeer(peer);
+ icu::AlphabeticIndex::ImmutableIndex* ii = immutableIndexFromPeer(peer);
return ii->getBucketCount();
}
static jint ImmutableIndex_getBucketIndex(JNIEnv* env, jclass, jlong peer, jstring javaString) {
- AlphabeticIndex::ImmutableIndex* ii = immutableIndexFromPeer(peer);
+ icu::AlphabeticIndex::ImmutableIndex* ii = immutableIndexFromPeer(peer);
ScopedJavaUnicodeString string(env, javaString);
if (!string.valid()) {
return -1;
@@ -167,8 +167,8 @@ static jint ImmutableIndex_getBucketIndex(JNIEnv* env, jclass, jlong peer, jstri
}
static jstring ImmutableIndex_getBucketLabel(JNIEnv* env, jclass, jlong peer, jint index) {
- AlphabeticIndex::ImmutableIndex* ii = immutableIndexFromPeer(peer);
- const AlphabeticIndex::Bucket* bucket = ii->getBucket(index);
+ icu::AlphabeticIndex::ImmutableIndex* ii = immutableIndexFromPeer(peer);
+ const icu::AlphabeticIndex::Bucket* bucket = ii->getBucket(index);
if (bucket == NULL) {
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Invalid index: %d", index);
return NULL;
@@ -179,7 +179,7 @@ static jstring ImmutableIndex_getBucketLabel(JNIEnv* env, jclass, jlong peer, ji
return env->NewStringUTF("");
}
- const UnicodeString& label(bucket->getLabel());
+ const icu::UnicodeString& label(bucket->getLabel());
return env->NewString(label.getBuffer(), label.length());
}
diff --git a/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp b/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp
deleted file mode 100644
index a3258c1..0000000
--- a/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "DateIntervalFormat"
-
-#include "IcuUtilities.h"
-#include "JniConstants.h"
-#include "ScopedIcuLocale.h"
-#include "ScopedJavaUnicodeString.h"
-#include "UniquePtr.h"
-#include "cutils/log.h"
-#include "unicode/dtitvfmt.h"
-
-static jlong DateIntervalFormat_createDateIntervalFormat(JNIEnv* env, jclass, jstring javaSkeleton, jstring javaLocaleName, jstring javaTzName) {
- ScopedIcuLocale icuLocale(env, javaLocaleName);
- if (!icuLocale.valid()) {
- return 0;
- }
-
- ScopedJavaUnicodeString skeletonHolder(env, javaSkeleton);
- if (!skeletonHolder.valid()) {
- return 0;
- }
-
- UErrorCode status = U_ZERO_ERROR;
- DateIntervalFormat* formatter(DateIntervalFormat::createInstance(skeletonHolder.unicodeString(), icuLocale.locale(), status));
- if (maybeThrowIcuException(env, "DateIntervalFormat::createInstance", status)) {
- return 0;
- }
-
- ScopedJavaUnicodeString tzNameHolder(env, javaTzName);
- if (!tzNameHolder.valid()) {
- return 0;
- }
- 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 s;
- FieldPosition pos = 0;
- UErrorCode status = U_ZERO_ERROR;
- formatter->format(&date_interval, s, pos, status);
- if (maybeThrowIcuException(env, "DateIntervalFormat::format", status)) {
- return NULL;
- }
-
- return env->NewString(s.getBuffer(), s.length());
-}
-
-static JNINativeMethod gMethods[] = {
- 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/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp
index d27b11d..0e744b7 100644
--- a/luni/src/main/native/libcore_icu_ICU.cpp
+++ b/luni/src/main/native/libcore_icu_ICU.cpp
@@ -25,7 +25,6 @@
#include "ScopedJavaUnicodeString.h"
#include "ScopedLocalRef.h"
#include "ScopedUtfChars.h"
-#include "UniquePtr.h"
#include "cutils/log.h"
#include "toStringArray.h"
#include "unicode/brkiter.h"
@@ -39,6 +38,7 @@
#include "unicode/locid.h"
#include "unicode/numfmt.h"
#include "unicode/strenum.h"
+#include "unicode/timezone.h"
#include "unicode/ubrk.h"
#include "unicode/ucal.h"
#include "unicode/uclean.h"
@@ -62,15 +62,9 @@
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
+#include <memory>
#include <vector>
-// TODO: put this in a header file and use it everywhere!
-// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions.
-// It goes in the private: declarations in a class.
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
- TypeName(const TypeName&); \
- void operator=(const TypeName&)
-
class ScopedResourceBundle {
public:
ScopedResourceBundle(UResourceBundle* bundle) : bundle_(bundle) {
@@ -121,7 +115,7 @@ static jint ICU_getCurrencyFractionDigits(JNIEnv* env, jclass, jstring javaCurre
if (!currencyCode.valid()) {
return 0;
}
- UnicodeString icuCurrencyCode(currencyCode.unicodeString());
+ icu::UnicodeString icuCurrencyCode(currencyCode.unicodeString());
UErrorCode status = U_ZERO_ERROR;
return ucurr_getDefaultFractionDigits(icuCurrencyCode.getTerminatedBuffer(), &status);
}
@@ -131,7 +125,7 @@ static jint ICU_getCurrencyNumericCode(JNIEnv* env, jclass, jstring javaCurrency
if (!currencyCode.valid()) {
return 0;
}
- UnicodeString icuCurrencyCode(currencyCode.unicodeString());
+ icu::UnicodeString icuCurrencyCode(currencyCode.unicodeString());
return ucurr_getNumericCode(icuCurrencyCode.getTerminatedBuffer());
}
@@ -187,7 +181,7 @@ static jstring getCurrencyName(JNIEnv* env, jstring javaLanguageTag, jstring jav
if (!currencyCode.valid()) {
return NULL;
}
- UnicodeString icuCurrencyCode(currencyCode.unicodeString());
+ icu::UnicodeString icuCurrencyCode(currencyCode.unicodeString());
UErrorCode status = U_ZERO_ERROR;
UBool isChoiceFormat = false;
int32_t charCount;
@@ -228,7 +222,7 @@ static jstring ICU_getDisplayCountryNative(JNIEnv* env, jclass, jstring javaTarg
return NULL;
}
- UnicodeString str;
+ icu::UnicodeString str;
icuTargetLocale.locale().getDisplayCountry(icuLocale.locale(), str);
return env->NewString(str.getBuffer(), str.length());
}
@@ -243,7 +237,7 @@ static jstring ICU_getDisplayLanguageNative(JNIEnv* env, jclass, jstring javaTar
return NULL;
}
- UnicodeString str;
+ icu::UnicodeString str;
icuTargetLocale.locale().getDisplayLanguage(icuLocale.locale(), str);
return env->NewString(str.getBuffer(), str.length());
}
@@ -258,7 +252,7 @@ static jstring ICU_getDisplayScriptNative(JNIEnv* env, jclass, jstring javaTarge
return NULL;
}
- UnicodeString str;
+ icu::UnicodeString str;
icuTargetLocale.locale().getDisplayScript(icuLocale.locale(), str);
return env->NewString(str.getBuffer(), str.length());
}
@@ -273,7 +267,7 @@ static jstring ICU_getDisplayVariantNative(JNIEnv* env, jclass, jstring javaTarg
return NULL;
}
- UnicodeString str;
+ icu::UnicodeString str;
icuTargetLocale.locale().getDisplayVariant(icuLocale.locale(), str);
return env->NewString(str.getBuffer(), str.length());
}
@@ -295,11 +289,11 @@ static jstring ICU_getISO3Language(JNIEnv* env, jclass, jstring javaLanguageTag)
}
static jobjectArray ICU_getISOCountriesNative(JNIEnv* env, jclass) {
- return toStringArray(env, Locale::getISOCountries());
+ return toStringArray(env, icu::Locale::getISOCountries());
}
static jobjectArray ICU_getISOLanguagesNative(JNIEnv* env, jclass) {
- return toStringArray(env, Locale::getISOLanguages());
+ return toStringArray(env, icu::Locale::getISOLanguages());
}
static jobjectArray ICU_getAvailableLocalesNative(JNIEnv* env, jclass) {
@@ -343,7 +337,7 @@ static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName,
env->SetObjectField(obj, fid, value);
}
-static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString* valueArray, int32_t size) {
+static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, const icu::UnicodeString* valueArray, int32_t size) {
ScopedLocalRef<jobjectArray> result(env, env->NewObjectArray(size, JniConstants::stringClass, NULL));
for (int32_t i = 0; i < size ; i++) {
ScopedLocalRef<jstring> s(env, env->NewString(valueArray[i].getBuffer(),valueArray[i].length()));
@@ -369,7 +363,7 @@ static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, URes
}
}
-static void setCharField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString& value) {
+static void setCharField(JNIEnv* env, jobject obj, const char* fieldName, const icu::UnicodeString& value) {
if (value.length() == 0) {
return;
}
@@ -377,43 +371,43 @@ static void setCharField(JNIEnv* env, jobject obj, const char* fieldName, const
env->SetCharField(obj, fid, value.charAt(0));
}
-static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, const UnicodeString& value) {
+static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, const icu::UnicodeString& value) {
const UChar* chars = value.getBuffer();
setStringField(env, obj, fieldName, env->NewString(chars, value.length()));
}
-static void setNumberPatterns(JNIEnv* env, jobject obj, Locale& locale) {
+static void setNumberPatterns(JNIEnv* env, jobject obj, icu::Locale& locale) {
UErrorCode status = U_ZERO_ERROR;
- UnicodeString pattern;
- UniquePtr<DecimalFormat> fmt(static_cast<DecimalFormat*>(NumberFormat::createInstance(locale, UNUM_CURRENCY, status)));
+ icu::UnicodeString pattern;
+ std::unique_ptr<icu::DecimalFormat> fmt(static_cast<icu::DecimalFormat*>(icu::NumberFormat::createInstance(locale, UNUM_CURRENCY, status)));
pattern = fmt->toPattern(pattern.remove());
setStringField(env, obj, "currencyPattern", pattern);
- fmt.reset(static_cast<DecimalFormat*>(NumberFormat::createInstance(locale, UNUM_DECIMAL, status)));
+ fmt.reset(static_cast<icu::DecimalFormat*>(icu::NumberFormat::createInstance(locale, UNUM_DECIMAL, status)));
pattern = fmt->toPattern(pattern.remove());
setStringField(env, obj, "numberPattern", pattern);
- fmt.reset(static_cast<DecimalFormat*>(NumberFormat::createInstance(locale, UNUM_PERCENT, status)));
+ fmt.reset(static_cast<icu::DecimalFormat*>(icu::NumberFormat::createInstance(locale, UNUM_PERCENT, status)));
pattern = fmt->toPattern(pattern.remove());
setStringField(env, obj, "percentPattern", pattern);
}
-static void setDecimalFormatSymbolsData(JNIEnv* env, jobject obj, Locale& locale) {
+static void setDecimalFormatSymbolsData(JNIEnv* env, jobject obj, icu::Locale& locale) {
UErrorCode status = U_ZERO_ERROR;
- DecimalFormatSymbols dfs(locale, status);
+ icu::DecimalFormatSymbols dfs(locale, status);
- setCharField(env, obj, "decimalSeparator", dfs.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol));
- setCharField(env, obj, "groupingSeparator", dfs.getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol));
- setCharField(env, obj, "patternSeparator", dfs.getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol));
- setStringField(env, obj, "percent", dfs.getSymbol(DecimalFormatSymbols::kPercentSymbol));
- setCharField(env, obj, "perMill", dfs.getSymbol(DecimalFormatSymbols::kPerMillSymbol));
- setCharField(env, obj, "monetarySeparator", dfs.getSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol));
- setStringField(env, obj, "minusSign", dfs.getSymbol(DecimalFormatSymbols:: kMinusSignSymbol));
- setStringField(env, obj, "exponentSeparator", dfs.getSymbol(DecimalFormatSymbols::kExponentialSymbol));
- setStringField(env, obj, "infinity", dfs.getSymbol(DecimalFormatSymbols::kInfinitySymbol));
- setStringField(env, obj, "NaN", dfs.getSymbol(DecimalFormatSymbols::kNaNSymbol));
- setCharField(env, obj, "zeroDigit", dfs.getSymbol(DecimalFormatSymbols::kZeroDigitSymbol));
+ setCharField(env, obj, "decimalSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kDecimalSeparatorSymbol));
+ setCharField(env, obj, "groupingSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kGroupingSeparatorSymbol));
+ setCharField(env, obj, "patternSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kPatternSeparatorSymbol));
+ setStringField(env, obj, "percent", dfs.getSymbol(icu::DecimalFormatSymbols::kPercentSymbol));
+ setCharField(env, obj, "perMill", dfs.getSymbol(icu::DecimalFormatSymbols::kPerMillSymbol));
+ setCharField(env, obj, "monetarySeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kMonetarySeparatorSymbol));
+ setStringField(env, obj, "minusSign", dfs.getSymbol(icu::DecimalFormatSymbols:: kMinusSignSymbol));
+ setStringField(env, obj, "exponentSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kExponentialSymbol));
+ setStringField(env, obj, "infinity", dfs.getSymbol(icu::DecimalFormatSymbols::kInfinitySymbol));
+ setStringField(env, obj, "NaN", dfs.getSymbol(icu::DecimalFormatSymbols::kNaNSymbol));
+ setCharField(env, obj, "zeroDigit", dfs.getSymbol(icu::DecimalFormatSymbols::kZeroDigitSymbol));
}
@@ -502,7 +496,7 @@ static bool getDateTimePatterns(JNIEnv* env, jobject localeData, const char* loc
return true;
}
-static bool getYesterdayTodayAndTomorrow(JNIEnv* env, jobject localeData, const Locale& locale, const char* locale_name) {
+static bool getYesterdayTodayAndTomorrow(JNIEnv* env, jobject localeData, const icu::Locale& locale, const char* locale_name) {
UErrorCode status = U_ZERO_ERROR;
ScopedResourceBundle root(ures_open(NULL, locale_name, &status));
ScopedResourceBundle fields(ures_getByKey(root.get(), "fields", NULL, &status));
@@ -512,16 +506,16 @@ static bool getYesterdayTodayAndTomorrow(JNIEnv* env, jobject localeData, const
return false;
}
- UnicodeString yesterday(ures_getUnicodeStringByKey(relative.get(), "-1", &status));
- UnicodeString today(ures_getUnicodeStringByKey(relative.get(), "0", &status));
- UnicodeString tomorrow(ures_getUnicodeStringByKey(relative.get(), "1", &status));
+ icu::UnicodeString yesterday(icu::ures_getUnicodeStringByKey(relative.get(), "-1", &status));
+ icu::UnicodeString today(icu::ures_getUnicodeStringByKey(relative.get(), "0", &status));
+ icu::UnicodeString tomorrow(icu::ures_getUnicodeStringByKey(relative.get(), "1", &status));
if (U_FAILURE(status)) {
ALOGE("Error getting yesterday/today/tomorrow for %s: %s", locale_name, u_errorName(status));
return false;
}
// We title-case the strings so they have consistent capitalization (http://b/14493853).
- UniquePtr<BreakIterator> brk(BreakIterator::createSentenceInstance(locale, status));
+ std::unique_ptr<icu::BreakIterator> brk(icu::BreakIterator::createSentenceInstance(locale, status));
if (U_FAILURE(status)) {
ALOGE("Error getting yesterday/today/tomorrow break iterator for %s: %s", locale_name, u_errorName(status));
return false;
@@ -591,7 +585,7 @@ static jboolean ICU_initLocaleDataNative(JNIEnv* env, jclass, jstring javaLangua
}
status = U_ZERO_ERROR;
- UniquePtr<Calendar> cal(Calendar::createInstance(icuLocale.locale(), status));
+ std::unique_ptr<icu::Calendar> cal(icu::Calendar::createInstance(icuLocale.locale(), status));
if (U_FAILURE(status)) {
return JNI_FALSE;
}
@@ -601,54 +595,54 @@ static jboolean ICU_initLocaleDataNative(JNIEnv* env, jclass, jstring javaLangua
// Get DateFormatSymbols.
status = U_ZERO_ERROR;
- DateFormatSymbols dateFormatSym(icuLocale.locale(), status);
+ icu::DateFormatSymbols dateFormatSym(icuLocale.locale(), status);
if (U_FAILURE(status)) {
return JNI_FALSE;
}
// Get AM/PM and BC/AD.
int32_t count = 0;
- const UnicodeString* amPmStrs = dateFormatSym.getAmPmStrings(count);
+ const icu::UnicodeString* amPmStrs = dateFormatSym.getAmPmStrings(count);
setStringArrayField(env, localeData, "amPm", amPmStrs, count);
- const UnicodeString* erasStrs = dateFormatSym.getEras(count);
+ const icu::UnicodeString* erasStrs = dateFormatSym.getEras(count);
setStringArrayField(env, localeData, "eras", erasStrs, count);
- const UnicodeString* longMonthNames =
- dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::WIDE);
+ const icu::UnicodeString* longMonthNames =
+ dateFormatSym.getMonths(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::WIDE);
setStringArrayField(env, localeData, "longMonthNames", longMonthNames, count);
- const UnicodeString* shortMonthNames =
- dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED);
+ const icu::UnicodeString* shortMonthNames =
+ dateFormatSym.getMonths(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::ABBREVIATED);
setStringArrayField(env, localeData, "shortMonthNames", shortMonthNames, count);
- const UnicodeString* tinyMonthNames =
- dateFormatSym.getMonths(count, DateFormatSymbols::FORMAT, DateFormatSymbols::NARROW);
+ const icu::UnicodeString* tinyMonthNames =
+ dateFormatSym.getMonths(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::NARROW);
setStringArrayField(env, localeData, "tinyMonthNames", tinyMonthNames, count);
- const UnicodeString* longWeekdayNames =
- dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::WIDE);
+ const icu::UnicodeString* longWeekdayNames =
+ dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::WIDE);
setStringArrayField(env, localeData, "longWeekdayNames", longWeekdayNames, count);
- const UnicodeString* shortWeekdayNames =
- dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED);
+ const icu::UnicodeString* shortWeekdayNames =
+ dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::ABBREVIATED);
setStringArrayField(env, localeData, "shortWeekdayNames", shortWeekdayNames, count);
- const UnicodeString* tinyWeekdayNames =
- dateFormatSym.getWeekdays(count, DateFormatSymbols::FORMAT, DateFormatSymbols::NARROW);
+ const icu::UnicodeString* tinyWeekdayNames =
+ dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::NARROW);
setStringArrayField(env, localeData, "tinyWeekdayNames", tinyWeekdayNames, count);
- const UnicodeString* longStandAloneMonthNames =
- dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE);
+ const icu::UnicodeString* longStandAloneMonthNames =
+ dateFormatSym.getMonths(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::WIDE);
setStringArrayField(env, localeData, "longStandAloneMonthNames", longStandAloneMonthNames, count);
- const UnicodeString* shortStandAloneMonthNames =
- dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED);
+ const icu::UnicodeString* shortStandAloneMonthNames =
+ dateFormatSym.getMonths(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::ABBREVIATED);
setStringArrayField(env, localeData, "shortStandAloneMonthNames", shortStandAloneMonthNames, count);
- const UnicodeString* tinyStandAloneMonthNames =
- dateFormatSym.getMonths(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::NARROW);
+ const icu::UnicodeString* tinyStandAloneMonthNames =
+ dateFormatSym.getMonths(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::NARROW);
setStringArrayField(env, localeData, "tinyStandAloneMonthNames", tinyStandAloneMonthNames, count);
- const UnicodeString* longStandAloneWeekdayNames =
- dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE);
+ const icu::UnicodeString* longStandAloneWeekdayNames =
+ dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::WIDE);
setStringArrayField(env, localeData, "longStandAloneWeekdayNames", longStandAloneWeekdayNames, count);
- const UnicodeString* shortStandAloneWeekdayNames =
- dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED);
+ const icu::UnicodeString* shortStandAloneWeekdayNames =
+ dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::ABBREVIATED);
setStringArrayField(env, localeData, "shortStandAloneWeekdayNames", shortStandAloneWeekdayNames, count);
- const UnicodeString* tinyStandAloneWeekdayNames =
- dateFormatSym.getWeekdays(count, DateFormatSymbols::STANDALONE, DateFormatSymbols::NARROW);
+ const icu::UnicodeString* tinyStandAloneWeekdayNames =
+ dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::NARROW);
setStringArrayField(env, localeData, "tinyStandAloneWeekdayNames", tinyStandAloneWeekdayNames, count);
status = U_ZERO_ERROR;
@@ -687,8 +681,8 @@ static jstring ICU_toLowerCase(JNIEnv* env, jclass, jstring javaString, jstring
if (!icuLocale.valid()) {
return NULL;
}
- UnicodeString& s(scopedString.unicodeString());
- UnicodeString original(s);
+ icu::UnicodeString& s(scopedString.unicodeString());
+ icu::UnicodeString original(s);
s.toLower(icuLocale.locale());
return s == original ? javaString : env->NewString(s.getBuffer(), s.length());
}
@@ -702,8 +696,8 @@ static jstring ICU_toUpperCase(JNIEnv* env, jclass, jstring javaString, jstring
if (!icuLocale.valid()) {
return NULL;
}
- UnicodeString& s(scopedString.unicodeString());
- UnicodeString original(s);
+ icu::UnicodeString& s(scopedString.unicodeString());
+ icu::UnicodeString original(s);
s.toUpper(icuLocale.locale());
return s == original ? javaString : env->NewString(s.getBuffer(), s.length());
}
@@ -733,9 +727,18 @@ static jstring ICU_getUnicodeVersion(JNIEnv* env, jclass) {
return versionString(env, unicodeVersion);
}
+static jstring ICU_getTZDataVersion(JNIEnv* env, jclass) {
+ UErrorCode status = U_ZERO_ERROR;
+ const char* version = icu::TimeZone::getTZDataVersion(status);
+ if (maybeThrowIcuException(env, "icu::TimeZone::getTZDataVersion", status)) {
+ return NULL;
+ }
+ return env->NewStringUTF(version);
+}
+
static jobject ICU_getAvailableCurrencyCodes(JNIEnv* env, jclass) {
UErrorCode status = U_ZERO_ERROR;
- UStringEnumeration e(ucurr_openISOCurrencies(UCURR_COMMON|UCURR_NON_DEPRECATED, &status));
+ icu::UStringEnumeration e(ucurr_openISOCurrencies(UCURR_COMMON|UCURR_NON_DEPRECATED, &status));
return fromStringEnumeration(env, status, "ucurr_openISOCurrencies", &e);
}
@@ -746,7 +749,7 @@ static jstring ICU_getBestDateTimePatternNative(JNIEnv* env, jclass, jstring jav
}
UErrorCode status = U_ZERO_ERROR;
- UniquePtr<DateTimePatternGenerator> generator(DateTimePatternGenerator::createInstance(icuLocale.locale(), status));
+ std::unique_ptr<icu::DateTimePatternGenerator> generator(icu::DateTimePatternGenerator::createInstance(icuLocale.locale(), status));
if (maybeThrowIcuException(env, "DateTimePatternGenerator::createInstance", status)) {
return NULL;
}
@@ -755,7 +758,7 @@ static jstring ICU_getBestDateTimePatternNative(JNIEnv* env, jclass, jstring jav
if (!skeletonHolder.valid()) {
return NULL;
}
- UnicodeString result(generator->getBestPattern(skeletonHolder.unicodeString(), status));
+ icu::UnicodeString result(generator->getBestPattern(skeletonHolder.unicodeString(), status));
if (maybeThrowIcuException(env, "DateTimePatternGenerator::getBestPattern", status)) {
return NULL;
}
@@ -770,12 +773,12 @@ static void ICU_setDefaultLocale(JNIEnv* env, jclass, jstring javaLanguageTag) {
}
UErrorCode status = U_ZERO_ERROR;
- Locale::setDefault(icuLocale.locale(), status);
+ icu::Locale::setDefault(icuLocale.locale(), status);
maybeThrowIcuException(env, "Locale::setDefault", status);
}
static jstring ICU_getDefaultLocale(JNIEnv* env, jclass) {
- return env->NewStringUTF(Locale::getDefault().getName());
+ return env->NewStringUTF(icu::Locale::getDefault().getName());
}
static JNINativeMethod gMethods[] = {
@@ -805,28 +808,25 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(ICU, getISOLanguagesNative, "()[Ljava/lang/String;"),
NATIVE_METHOD(ICU, getIcuVersion, "()Ljava/lang/String;"),
NATIVE_METHOD(ICU, getScript, "(Ljava/lang/String;)Ljava/lang/String;"),
+ NATIVE_METHOD(ICU, getTZDataVersion, "()Ljava/lang/String;"),
NATIVE_METHOD(ICU, getUnicodeVersion, "()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;"),
NATIVE_METHOD(ICU, toUpperCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
};
-void register_libcore_icu_ICU(JNIEnv* env) {
- std::string path;
- path = u_getDataDirectory();
- path += "/";
- path += U_ICUDATA_NAME;
- path += ".dat";
-
- #define FAIL_WITH_STRERROR(s) \
- ALOGE("Couldn't " s " '%s': %s", path.c_str(), strerror(errno)); \
- abort();
- #define MAYBE_FAIL_WITH_ICU_ERROR(s) \
- if (status != U_ZERO_ERROR) {\
- ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path.c_str()); \
- abort(); \
- }
+#define FAIL_WITH_STRERROR(s) \
+ ALOGE("Couldn't " s " '%s': %s", path.c_str(), strerror(errno)); \
+ return FALSE;
+
+#define MAYBE_FAIL_WITH_ICU_ERROR(s) \
+ if (status != U_ZERO_ERROR) {\
+ ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path.c_str()); \
+ return FALSE; \
+ }
+
+static bool mapIcuData(const std::string& path) {
// Open the file and get its length.
ScopedFd fd(open(path.c_str(), O_RDONLY));
if (fd.get() == -1) {
@@ -848,18 +848,66 @@ void register_libcore_icu_ICU(JNIEnv* env) {
FAIL_WITH_STRERROR("madvise(MADV_RANDOM)");
}
- // Tell ICU to use our memory-mapped data.
UErrorCode status = U_ZERO_ERROR;
+
+ // Tell ICU to use our memory-mapped data.
udata_setCommonData(data, &status);
MAYBE_FAIL_WITH_ICU_ERROR("udata_setCommonData");
+
+ return TRUE;
+}
+
+void register_libcore_icu_ICU(JNIEnv* env) {
+ // Check the timezone override file exists. If it does, map it first so we use it in preference
+ // to the one that shipped with the device.
+ const char* dataPathPrefix = getenv("ANDROID_DATA");
+ if (dataPathPrefix == NULL) {
+ ALOGE("ANDROID_DATA environment variable not set"); \
+ abort();
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
// Tell ICU it can *only* use our memory-mapped data.
udata_setFileAccess(UDATA_NO_FILES, &status);
- MAYBE_FAIL_WITH_ICU_ERROR("udata_setFileAccess");
+ if (status != U_ZERO_ERROR) {
+ ALOGE("Couldn't initialize ICU (s_setFileAccess): %s", u_errorName(status));
+ abort();
+ }
+
+ // Map in optional TZ data files.
+ std::string dataPath;
+ dataPath = dataPathPrefix;
+ dataPath += "/misc/zoneinfo/current/icu/icu_tzdata.dat";
+
+ struct stat sb;
+ if (stat(dataPath.c_str(), &sb) == 0) {
+ ALOGD("Timezone override file found: %s", dataPath.c_str());
+ if (!mapIcuData(dataPath)) {
+ ALOGW("TZ override file %s exists but could not be loaded. Skipping.", dataPath.c_str());
+ }
+ } else {
+ ALOGD("No timezone override file found: %s", dataPath.c_str());
+ }
+
+ // Use the ICU data files that shipped with the device for everything else.
+ std::string systemPath;
+ systemPath = u_getDataDirectory();
+ systemPath += "/";
+ systemPath += U_ICUDATA_NAME;
+ systemPath += ".dat";
+
+ if (!mapIcuData(systemPath)) {
+ abort();
+ }
// Failures to find the ICU data tend to be somewhat obscure because ICU loads its data on first
// use, which can be anywhere. Force initialization up front so we can report a nice clear error
// and bail.
u_init(&status);
- MAYBE_FAIL_WITH_ICU_ERROR("u_init");
+ if (status != U_ZERO_ERROR) {\
+ ALOGE("Couldn't initialize ICU (u_init): %s", u_errorName(status));
+ abort();
+ }
+
jniRegisterNativeMethods(env, "libcore/icu/ICU", gMethods, NELEM(gMethods));
}
diff --git a/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp b/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
deleted file mode 100644
index ef0c2a9..0000000
--- a/luni/src/main/native/libcore_icu_NativeBreakIterator.cpp
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "NativeBreakIterator"
-
-#include "IcuUtilities.h"
-#include "JNIHelp.h"
-#include "JniConstants.h"
-#include "JniException.h"
-#include "ScopedIcuLocale.h"
-#include "ScopedUtfChars.h"
-#include "unicode/brkiter.h"
-#include "unicode/putil.h"
-#include <stdlib.h>
-
-// ICU documentation: http://icu-project.org/apiref/icu4c/classBreakIterator.html
-
-static BreakIterator* toBreakIterator(jlong address) {
- return reinterpret_cast<BreakIterator*>(static_cast<uintptr_t>(address));
-}
-
-/**
- * We use ICU4C's BreakIterator class, but our input is on the Java heap and potentially moving
- * around between calls. This wrapper class ensures that our RegexMatcher is always pointing at
- * the current location of the char[]. Earlier versions of Android simply copied the data to the
- * native heap, but that's wasteful and hides allocations from the garbage collector.
- */
-class BreakIteratorAccessor {
- public:
- BreakIteratorAccessor(JNIEnv* env, jlong address, jstring javaInput, bool reset) {
- init(env, address);
- mJavaInput = javaInput;
-
- if (mJavaInput == NULL) {
- return;
- }
-
- mChars = env->GetStringChars(mJavaInput, NULL);
- if (mChars == NULL) {
- return;
- }
-
- mUText = utext_openUChars(NULL, mChars, env->GetStringLength(mJavaInput), &mStatus);
- if (mUText == NULL) {
- return;
- }
-
- if (reset) {
- mBreakIterator->setText(mUText, mStatus);
- } else {
- mBreakIterator->refreshInputText(mUText, mStatus);
- }
- }
-
- BreakIteratorAccessor(JNIEnv* env, jlong address) {
- init(env, address);
- }
-
- ~BreakIteratorAccessor() {
- utext_close(mUText);
- if (mJavaInput) {
- mEnv->ReleaseStringChars(mJavaInput, mChars);
- }
- maybeThrowIcuException(mEnv, "utext_close", mStatus);
- }
-
- BreakIterator* operator->() {
- return mBreakIterator;
- }
-
- UErrorCode& status() {
- return mStatus;
- }
-
- private:
- void init(JNIEnv* env, jlong address) {
- mEnv = env;
- mJavaInput = NULL;
- mBreakIterator = toBreakIterator(address);
- mChars = NULL;
- mStatus = U_ZERO_ERROR;
- mUText = NULL;
- }
-
- JNIEnv* mEnv;
- jstring mJavaInput;
- BreakIterator* mBreakIterator;
- const jchar* mChars;
- UErrorCode mStatus;
- UText* mUText;
-
- // Disallow copy and assignment.
- BreakIteratorAccessor(const BreakIteratorAccessor&);
- void operator=(const BreakIteratorAccessor&);
-};
-
-#define MAKE_BREAK_ITERATOR_INSTANCE(F) \
- ScopedIcuLocale icuLocale(env, javaLocaleName); \
- if (!icuLocale.valid()) { \
- return 0; \
- } \
- UErrorCode status = U_ZERO_ERROR; \
- BreakIterator* it = F(icuLocale.locale(), status); \
- if (maybeThrowIcuException(env, "ubrk_open", status)) { \
- return 0; \
- } \
- return reinterpret_cast<uintptr_t>(it)
-
-static jlong NativeBreakIterator_cloneImpl(JNIEnv* env, jclass, jlong address) {
- BreakIteratorAccessor it(env, address);
- return reinterpret_cast<uintptr_t>(it->clone());
-}
-
-static void NativeBreakIterator_closeImpl(JNIEnv*, jclass, jlong address) {
- delete toBreakIterator(address);
-}
-
-static jint NativeBreakIterator_currentImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) {
- BreakIteratorAccessor it(env, address, javaInput, false);
- return it->current();
-}
-
-static jint NativeBreakIterator_firstImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) {
- BreakIteratorAccessor it(env, address, javaInput, false);
- return it->first();
-}
-
-static jint NativeBreakIterator_followingImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint offset) {
- BreakIteratorAccessor it(env, address, javaInput, false);
- return it->following(offset);
-}
-
-static jlong NativeBreakIterator_getCharacterInstanceImpl(JNIEnv* env, jclass, jstring javaLocaleName) {
- MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createCharacterInstance);
-}
-
-static jlong NativeBreakIterator_getLineInstanceImpl(JNIEnv* env, jclass, jstring javaLocaleName) {
- MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createLineInstance);
-}
-
-static jlong NativeBreakIterator_getSentenceInstanceImpl(JNIEnv* env, jclass, jstring javaLocaleName) {
- MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createSentenceInstance);
-}
-
-static jlong NativeBreakIterator_getWordInstanceImpl(JNIEnv* env, jclass, jstring javaLocaleName) {
- MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createWordInstance);
-}
-
-static jboolean NativeBreakIterator_isBoundaryImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint offset) {
- BreakIteratorAccessor it(env, address, javaInput, false);
- return it->isBoundary(offset);
-}
-
-static jint NativeBreakIterator_lastImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) {
- BreakIteratorAccessor it(env, address, javaInput, false);
- return it->last();
-}
-
-static jint NativeBreakIterator_nextImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint n) {
- BreakIteratorAccessor it(env, address, javaInput, false);
- if (n < 0) {
- while (n++ < -1) {
- it->previous();
- }
- return it->previous();
- } else if (n == 0) {
- return it->current();
- } else {
- while (n-- > 1) {
- it->next();
- }
- return it->next();
- }
- return -1;
-}
-
-static jint NativeBreakIterator_precedingImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint offset) {
- BreakIteratorAccessor it(env, address, javaInput, false);
- return it->preceding(offset);
-}
-
-static jint NativeBreakIterator_previousImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) {
- BreakIteratorAccessor it(env, address, javaInput, false);
- return it->previous();
-}
-
-static void NativeBreakIterator_setTextImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) {
- BreakIteratorAccessor it(env, address, javaInput, true);
-}
-
-static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(NativeBreakIterator, cloneImpl, "(J)J"),
- NATIVE_METHOD(NativeBreakIterator, closeImpl, "(J)V"),
- NATIVE_METHOD(NativeBreakIterator, currentImpl, "(JLjava/lang/String;)I"),
- NATIVE_METHOD(NativeBreakIterator, firstImpl, "(JLjava/lang/String;)I"),
- NATIVE_METHOD(NativeBreakIterator, followingImpl, "(JLjava/lang/String;I)I"),
- NATIVE_METHOD(NativeBreakIterator, getCharacterInstanceImpl, "(Ljava/lang/String;)J"),
- NATIVE_METHOD(NativeBreakIterator, getLineInstanceImpl, "(Ljava/lang/String;)J"),
- NATIVE_METHOD(NativeBreakIterator, getSentenceInstanceImpl, "(Ljava/lang/String;)J"),
- NATIVE_METHOD(NativeBreakIterator, getWordInstanceImpl, "(Ljava/lang/String;)J"),
- NATIVE_METHOD(NativeBreakIterator, isBoundaryImpl, "(JLjava/lang/String;I)Z"),
- NATIVE_METHOD(NativeBreakIterator, lastImpl, "(JLjava/lang/String;)I"),
- NATIVE_METHOD(NativeBreakIterator, nextImpl, "(JLjava/lang/String;I)I"),
- NATIVE_METHOD(NativeBreakIterator, precedingImpl, "(JLjava/lang/String;I)I"),
- NATIVE_METHOD(NativeBreakIterator, previousImpl, "(JLjava/lang/String;)I"),
- NATIVE_METHOD(NativeBreakIterator, setTextImpl, "(JLjava/lang/String;)V"),
-};
-void register_libcore_icu_NativeBreakIterator(JNIEnv* env) {
- jniRegisterNativeMethods(env, "libcore/icu/NativeBreakIterator", gMethods, NELEM(gMethods));
-}
diff --git a/luni/src/main/native/libcore_icu_NativeCollation.cpp b/luni/src/main/native/libcore_icu_NativeCollation.cpp
index 4ce42ec..f27d72e 100644
--- a/luni/src/main/native/libcore_icu_NativeCollation.cpp
+++ b/luni/src/main/native/libcore_icu_NativeCollation.cpp
@@ -15,10 +15,10 @@
#include "JniException.h"
#include "ScopedStringChars.h"
#include "ScopedUtfChars.h"
-#include "UniquePtr.h"
#include "unicode/ucol.h"
#include "unicode/ucoleitr.h"
#include <cutils/log.h>
+#include <memory>
// Manages a UCollationElements instance along with the jchar
// array it is iterating over. The associated array can be unpinned
@@ -124,7 +124,7 @@ static jlong NativeCollation_getCollationElementIterator(JNIEnv* env, jclass, jl
return -1;
}
- UniquePtr<CollationElements> ce(new CollationElements);
+ std::unique_ptr<CollationElements> ce(new CollationElements);
UErrorCode status = ce->start(env, javaSource, toCollator(address));
maybeThrowIcuException(env, "ucol_openElements", status);
if (status == U_ZERO_ERROR) {
@@ -156,7 +156,7 @@ static jbyteArray NativeCollation_getSortKey(JNIEnv* env, jclass, jlong address,
const UCollator* collator = toCollator(address);
// The buffer size prevents reallocation for most strings.
uint8_t byteArray[128];
- UniquePtr<uint8_t[]> largerByteArray;
+ std::unique_ptr<uint8_t[]> largerByteArray;
uint8_t* usedByteArray = byteArray;
size_t byteArraySize = ucol_getSortKey(collator, source.get(), source.size(), usedByteArray, sizeof(byteArray) - 1);
if (byteArraySize > sizeof(byteArray) - 1) {
diff --git a/luni/src/main/native/libcore_icu_NativeConverter.cpp b/luni/src/main/native/libcore_icu_NativeConverter.cpp
index 8dd439a..355cc78 100644
--- a/luni/src/main/native/libcore_icu_NativeConverter.cpp
+++ b/luni/src/main/native/libcore_icu_NativeConverter.cpp
@@ -23,7 +23,6 @@
#include "ScopedPrimitiveArray.h"
#include "ScopedStringChars.h"
#include "ScopedUtfChars.h"
-#include "UniquePtr.h"
#include "cutils/log.h"
#include "toStringArray.h"
#include "unicode/ucnv.h"
@@ -32,6 +31,7 @@
#include "unicode/ustring.h"
#include "unicode/utypes.h"
+#include <memory>
#include <vector>
#include <stdlib.h>
@@ -64,7 +64,7 @@ static UConverter* toUConverter(jlong address) {
static bool collectStandardNames(JNIEnv* env, const char* canonicalName, const char* standard,
std::vector<std::string>& result) {
UErrorCode status = U_ZERO_ERROR;
- UStringEnumeration e(ucnv_openStandardNames(canonicalName, standard, &status));
+ icu::UStringEnumeration e(ucnv_openStandardNames(canonicalName, standard, &status));
if (maybeThrowIcuException(env, "ucnv_openStandardNames", status)) {
return false;
}
@@ -75,7 +75,7 @@ static bool collectStandardNames(JNIEnv* env, const char* canonicalName, const c
}
for (int32_t i = 0; i < count; ++i) {
- const UnicodeString* string = e.snext(status);
+ const icu::UnicodeString* string = e.snext(status);
if (maybeThrowIcuException(env, "StringEnumeration::snext", status)) {
return false;
}
@@ -104,7 +104,7 @@ static const char* getICUCanonicalName(const char* name) {
} else if (strstr(name, "x-") == name) {
// Check if the converter can be opened with the name given.
error = U_ZERO_ERROR;
- LocalUConverterPointer cnv(ucnv_open(name + 2, &error));
+ icu::LocalUConverterPointer cnv(ucnv_open(name + 2, &error));
if (U_SUCCESS(error)) {
return name + 2;
}
@@ -150,7 +150,7 @@ static jstring getJavaCanonicalName(JNIEnv* env, const char* icuCanonicalName) {
if (name == NULL) {
name = icuCanonicalName;
}
- UniquePtr<char[]> result(new char[2 + strlen(name) + 1]);
+ std::unique_ptr<char[]> result(new char[2 + strlen(name) + 1]);
strcpy(&result[0], "x-");
strcat(&result[0], name);
return env->NewStringUTF(&result[0]);
@@ -537,12 +537,12 @@ static jboolean NativeConverter_contains(JNIEnv* env, jclass, jstring name1, jst
}
UErrorCode errorCode = U_ZERO_ERROR;
- LocalUConverterPointer converter1(ucnv_open(name1Chars.c_str(), &errorCode));
- UnicodeSet set1;
+ icu::LocalUConverterPointer converter1(ucnv_open(name1Chars.c_str(), &errorCode));
+ icu::UnicodeSet set1;
ucnv_getUnicodeSet(&*converter1, set1.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
- LocalUConverterPointer converter2(ucnv_open(name2Chars.c_str(), &errorCode));
- UnicodeSet set2;
+ icu::LocalUConverterPointer converter2(ucnv_open(name2Chars.c_str(), &errorCode));
+ icu::UnicodeSet set2;
ucnv_getUnicodeSet(&*converter2, set2.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
return U_SUCCESS(errorCode) && set1.containsAll(set2);
@@ -570,7 +570,7 @@ static jobject NativeConverter_charsetForName(JNIEnv* env, jclass, jstring chars
{
// ICU doesn't offer any "isSupported", so we just open and immediately close.
UErrorCode error = U_ZERO_ERROR;
- LocalUConverterPointer cnv(ucnv_open(icuCanonicalName, &error));
+ icu::LocalUConverterPointer cnv(ucnv_open(icuCanonicalName, &error));
if (!U_SUCCESS(error)) {
return NULL;
}
diff --git a/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp b/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
index 8e440e9..8c4a411 100644
--- a/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
+++ b/luni/src/main/native/libcore_icu_NativeDecimalFormat.cpp
@@ -19,6 +19,7 @@
#include <stdlib.h>
#include <string.h>
+#include <memory>
#include <vector>
#include "cutils/log.h"
@@ -36,18 +37,17 @@
#include "unicode/numfmt.h"
#include "unicode/unum.h"
#include "unicode/ustring.h"
-#include "UniquePtr.h"
#include "valueOf.h"
-static DecimalFormat* toDecimalFormat(jlong addr) {
- return reinterpret_cast<DecimalFormat*>(static_cast<uintptr_t>(addr));
+static icu::DecimalFormat* toDecimalFormat(jlong addr) {
+ return reinterpret_cast<icu::DecimalFormat*>(static_cast<uintptr_t>(addr));
}
static UNumberFormat* toUNumberFormat(jlong addr) {
return reinterpret_cast<UNumberFormat*>(static_cast<uintptr_t>(addr));
}
-static DecimalFormatSymbols* makeDecimalFormatSymbols(JNIEnv* env,
+static icu::DecimalFormatSymbols* makeDecimalFormatSymbols(JNIEnv* env,
jstring currencySymbol0, jchar decimalSeparator, jchar digit, jstring exponentSeparator0,
jchar groupingSeparator0, jstring infinity0,
jstring internationalCurrencySymbol0, jstring minusSign0,
@@ -60,36 +60,41 @@ static DecimalFormatSymbols* makeDecimalFormatSymbols(JNIEnv* env,
ScopedJavaUnicodeString nan(env, nan0);
ScopedJavaUnicodeString minusSign(env, minusSign0);
ScopedJavaUnicodeString percent(env, percent0);
- UnicodeString groupingSeparator(groupingSeparator0);
-
- DecimalFormatSymbols* result = new DecimalFormatSymbols;
- result->setSymbol(DecimalFormatSymbols::kCurrencySymbol, currencySymbol.unicodeString());
- result->setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, UnicodeString(decimalSeparator));
- result->setSymbol(DecimalFormatSymbols::kDigitSymbol, UnicodeString(digit));
- result->setSymbol(DecimalFormatSymbols::kExponentialSymbol, exponentSeparator.unicodeString());
- result->setSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol, groupingSeparator);
- result->setSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol, groupingSeparator);
- result->setSymbol(DecimalFormatSymbols::kInfinitySymbol, infinity.unicodeString());
- result->setSymbol(DecimalFormatSymbols::kIntlCurrencySymbol, internationalCurrencySymbol.unicodeString());
- result->setSymbol(DecimalFormatSymbols::kMinusSignSymbol, minusSign.unicodeString());
- result->setSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol, UnicodeString(monetaryDecimalSeparator));
- result->setSymbol(DecimalFormatSymbols::kNaNSymbol, nan.unicodeString());
- result->setSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol, UnicodeString(patternSeparator));
- result->setSymbol(DecimalFormatSymbols::kPercentSymbol, percent.unicodeString());
- result->setSymbol(DecimalFormatSymbols::kPerMillSymbol, UnicodeString(perMill));
+ icu::UnicodeString groupingSeparator(groupingSeparator0);
+
+ UErrorCode status = U_ZERO_ERROR;
+ std::unique_ptr<icu::DecimalFormatSymbols> result(icu::DecimalFormatSymbols::createWithLastResortData(status));
+ if (maybeThrowIcuException(env, "DecimalFormatSymbols::createWithLastResortData", status)) {
+ return NULL;
+ }
+
+ result->setSymbol(icu::DecimalFormatSymbols::kCurrencySymbol, currencySymbol.unicodeString());
+ result->setSymbol(icu::DecimalFormatSymbols::kDecimalSeparatorSymbol, icu::UnicodeString(decimalSeparator));
+ result->setSymbol(icu::DecimalFormatSymbols::kDigitSymbol, icu::UnicodeString(digit));
+ result->setSymbol(icu::DecimalFormatSymbols::kExponentialSymbol, exponentSeparator.unicodeString());
+ result->setSymbol(icu::DecimalFormatSymbols::kGroupingSeparatorSymbol, groupingSeparator);
+ result->setSymbol(icu::DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol, groupingSeparator);
+ result->setSymbol(icu::DecimalFormatSymbols::kInfinitySymbol, infinity.unicodeString());
+ result->setSymbol(icu::DecimalFormatSymbols::kIntlCurrencySymbol, internationalCurrencySymbol.unicodeString());
+ result->setSymbol(icu::DecimalFormatSymbols::kMinusSignSymbol, minusSign.unicodeString());
+ result->setSymbol(icu::DecimalFormatSymbols::kMonetarySeparatorSymbol, icu::UnicodeString(monetaryDecimalSeparator));
+ result->setSymbol(icu::DecimalFormatSymbols::kNaNSymbol, nan.unicodeString());
+ result->setSymbol(icu::DecimalFormatSymbols::kPatternSeparatorSymbol, icu::UnicodeString(patternSeparator));
+ result->setSymbol(icu::DecimalFormatSymbols::kPercentSymbol, percent.unicodeString());
+ result->setSymbol(icu::DecimalFormatSymbols::kPerMillSymbol, icu::UnicodeString(perMill));
// java.text.DecimalFormatSymbols just uses a zero digit,
// but ICU >= 4.6 has a field for each decimal digit.
- result->setSymbol(DecimalFormatSymbols::kZeroDigitSymbol, UnicodeString(zeroDigit + 0));
- result->setSymbol(DecimalFormatSymbols::kOneDigitSymbol, UnicodeString(zeroDigit + 1));
- result->setSymbol(DecimalFormatSymbols::kTwoDigitSymbol, UnicodeString(zeroDigit + 2));
- result->setSymbol(DecimalFormatSymbols::kThreeDigitSymbol, UnicodeString(zeroDigit + 3));
- result->setSymbol(DecimalFormatSymbols::kFourDigitSymbol, UnicodeString(zeroDigit + 4));
- result->setSymbol(DecimalFormatSymbols::kFiveDigitSymbol, UnicodeString(zeroDigit + 5));
- result->setSymbol(DecimalFormatSymbols::kSixDigitSymbol, UnicodeString(zeroDigit + 6));
- result->setSymbol(DecimalFormatSymbols::kSevenDigitSymbol, UnicodeString(zeroDigit + 7));
- result->setSymbol(DecimalFormatSymbols::kEightDigitSymbol, UnicodeString(zeroDigit + 8));
- result->setSymbol(DecimalFormatSymbols::kNineDigitSymbol, UnicodeString(zeroDigit + 9));
- return result;
+ result->setSymbol(icu::DecimalFormatSymbols::kZeroDigitSymbol, icu::UnicodeString(zeroDigit + 0));
+ result->setSymbol(icu::DecimalFormatSymbols::kOneDigitSymbol, icu::UnicodeString(zeroDigit + 1));
+ result->setSymbol(icu::DecimalFormatSymbols::kTwoDigitSymbol, icu::UnicodeString(zeroDigit + 2));
+ result->setSymbol(icu::DecimalFormatSymbols::kThreeDigitSymbol, icu::UnicodeString(zeroDigit + 3));
+ result->setSymbol(icu::DecimalFormatSymbols::kFourDigitSymbol, icu::UnicodeString(zeroDigit + 4));
+ result->setSymbol(icu::DecimalFormatSymbols::kFiveDigitSymbol, icu::UnicodeString(zeroDigit + 5));
+ result->setSymbol(icu::DecimalFormatSymbols::kSixDigitSymbol, icu::UnicodeString(zeroDigit + 6));
+ result->setSymbol(icu::DecimalFormatSymbols::kSevenDigitSymbol, icu::UnicodeString(zeroDigit + 7));
+ result->setSymbol(icu::DecimalFormatSymbols::kEightDigitSymbol, icu::UnicodeString(zeroDigit + 8));
+ result->setSymbol(icu::DecimalFormatSymbols::kNineDigitSymbol, icu::UnicodeString(zeroDigit + 9));
+ return result.release();
}
static void NativeDecimalFormat_setDecimalFormatSymbols(JNIEnv* env, jclass, jlong addr,
@@ -98,7 +103,7 @@ static void NativeDecimalFormat_setDecimalFormatSymbols(JNIEnv* env, jclass, jlo
jstring internationalCurrencySymbol, jstring minusSign,
jchar monetaryDecimalSeparator, jstring nan, jchar patternSeparator,
jstring percent, jchar perMill, jchar zeroDigit) {
- DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env,
+ icu::DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env,
currencySymbol, decimalSeparator, digit, exponentSeparator, groupingSeparator,
infinity, internationalCurrencySymbol, minusSign,
monetaryDecimalSeparator, nan, patternSeparator, percent, perMill,
@@ -118,12 +123,12 @@ static jlong NativeDecimalFormat_open(JNIEnv* env, jclass, jstring pattern0,
if (!pattern.valid()) {
return 0;
}
- DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env,
+ icu::DecimalFormatSymbols* symbols = makeDecimalFormatSymbols(env,
currencySymbol, decimalSeparator, digit, exponentSeparator, groupingSeparator,
infinity, internationalCurrencySymbol, minusSign,
monetaryDecimalSeparator, nan, patternSeparator, percent, perMill,
zeroDigit);
- DecimalFormat* fmt = new DecimalFormat(pattern.unicodeString(), symbols, parseError, status);
+ icu::DecimalFormat* fmt = new icu::DecimalFormat(pattern.unicodeString(), symbols, parseError, status);
if (fmt == NULL) {
delete symbols;
}
@@ -136,8 +141,8 @@ static void NativeDecimalFormat_close(JNIEnv*, jclass, jlong addr) {
}
static void NativeDecimalFormat_setRoundingMode(JNIEnv*, jclass, jlong addr, jint mode, jdouble increment) {
- DecimalFormat* fmt = toDecimalFormat(addr);
- fmt->setRoundingMode(static_cast<DecimalFormat::ERoundingMode>(mode));
+ icu::DecimalFormat* fmt = toDecimalFormat(addr);
+ fmt->setRoundingMode(static_cast<icu::DecimalFormat::ERoundingMode>(mode));
fmt->setRoundingIncrement(increment);
}
@@ -179,7 +184,7 @@ static jstring NativeDecimalFormat_getTextAttribute(JNIEnv* env, jclass, jlong a
UNumberFormatTextAttribute attr = static_cast<UNumberFormatTextAttribute>(javaAttr);
// Find out how long the result will be...
- UniquePtr<UChar[]> chars;
+ std::unique_ptr<UChar[]> chars;
uint32_t charCount = 0;
uint32_t desiredCount = unum_getTextAttribute(fmt, attr, chars.get(), charCount, &status);
if (status == U_BUFFER_OVERFLOW_ERROR) {
@@ -197,7 +202,7 @@ static void NativeDecimalFormat_applyPatternImpl(JNIEnv* env, jclass, jlong addr
if (!pattern.valid()) {
return;
}
- DecimalFormat* fmt = toDecimalFormat(addr);
+ icu::DecimalFormat* fmt = toDecimalFormat(addr);
UErrorCode status = U_ZERO_ERROR;
const char* function;
if (localized) {
@@ -211,8 +216,8 @@ static void NativeDecimalFormat_applyPatternImpl(JNIEnv* env, jclass, jlong addr
}
static jstring NativeDecimalFormat_toPatternImpl(JNIEnv* env, jclass, jlong addr, jboolean localized) {
- DecimalFormat* fmt = toDecimalFormat(addr);
- UnicodeString pattern;
+ icu::DecimalFormat* fmt = toDecimalFormat(addr);
+ icu::UnicodeString pattern;
if (localized) {
fmt->toLocalizedPattern(pattern);
} else {
@@ -221,12 +226,12 @@ static jstring NativeDecimalFormat_toPatternImpl(JNIEnv* env, jclass, jlong addr
return env->NewString(pattern.getBuffer(), pattern.length());
}
-static jcharArray formatResult(JNIEnv* env, const UnicodeString& s, FieldPositionIterator* fpi, jobject javaFieldPositionIterator) {
+static jcharArray formatResult(JNIEnv* env, const icu::UnicodeString& s, icu::FieldPositionIterator* fpi, jobject javaFieldPositionIterator) {
static jmethodID gFPI_setData = env->GetMethodID(JniConstants::fieldPositionIteratorClass, "setData", "([I)V");
if (fpi != NULL) {
std::vector<int32_t> data;
- FieldPosition fp;
+ icu::FieldPosition fp;
while (fpi->next(fp)) {
data.push_back(fp.getField());
data.push_back(fp.getBeginIndex());
@@ -258,10 +263,10 @@ static jcharArray formatResult(JNIEnv* env, const UnicodeString& s, FieldPositio
template <typename T>
static jcharArray format(JNIEnv* env, jlong addr, jobject javaFieldPositionIterator, T value) {
UErrorCode status = U_ZERO_ERROR;
- UnicodeString s;
- DecimalFormat* fmt = toDecimalFormat(addr);
- FieldPositionIterator nativeFieldPositionIterator;
- FieldPositionIterator* fpi = javaFieldPositionIterator ? &nativeFieldPositionIterator : NULL;
+ icu::UnicodeString s;
+ icu::DecimalFormat* fmt = toDecimalFormat(addr);
+ icu::FieldPositionIterator nativeFieldPositionIterator;
+ icu::FieldPositionIterator* fpi = javaFieldPositionIterator ? &nativeFieldPositionIterator : NULL;
fmt->format(value, s, fpi, status);
if (maybeThrowIcuException(env, "DecimalFormat::format", status)) {
return NULL;
@@ -282,7 +287,7 @@ static jcharArray NativeDecimalFormat_formatDigitList(JNIEnv* env, jclass, jlong
if (chars.c_str() == NULL) {
return NULL;
}
- StringPiece sp(chars.c_str());
+ icu::StringPiece sp(chars.c_str());
return format(env, addr, javaFieldPositionIterator, sp);
}
@@ -293,7 +298,7 @@ static jobject newBigDecimal(JNIEnv* env, const char* value, jsize len) {
// value is a UTF-8 string of invariant characters, but isn't guaranteed to be
// null-terminated. NewStringUTF requires a terminated UTF-8 string. So we copy the
// data to jchars using UnicodeString, and call NewString instead.
- UnicodeString tmp(value, len, UnicodeString::kInvariant);
+ icu::UnicodeString tmp(value, len, icu::UnicodeString::kInvariant);
jobject str = env->NewString(tmp.getBuffer(), tmp.length());
return env->NewObject(JniConstants::bigDecimalClass, gBigDecimal_init, str);
}
@@ -318,9 +323,9 @@ static jobject NativeDecimalFormat_parse(JNIEnv* env, jclass, jlong addr, jstrin
return NULL;
}
- Formattable res;
- ParsePosition pp(parsePos);
- DecimalFormat* fmt = toDecimalFormat(addr);
+ icu::Formattable res;
+ icu::ParsePosition pp(parsePos);
+ icu::DecimalFormat* fmt = toDecimalFormat(addr);
fmt->parse(src.unicodeString(), res, pp);
if (pp.getErrorIndex() == -1) {
@@ -332,7 +337,7 @@ static jobject NativeDecimalFormat_parse(JNIEnv* env, jclass, jlong addr, jstrin
if (parseBigDecimal) {
UErrorCode status = U_ZERO_ERROR;
- StringPiece str = res.getDecimalNumber(status);
+ icu::StringPiece str = res.getDecimalNumber(status);
if (U_SUCCESS(status)) {
int len = str.length();
const char* data = str.data();
@@ -348,15 +353,15 @@ static jobject NativeDecimalFormat_parse(JNIEnv* env, jclass, jlong addr, jstrin
}
switch (res.getType()) {
- case Formattable::kDouble: return doubleValueOf(env, res.getDouble());
- case Formattable::kLong: return longValueOf(env, res.getLong());
- case Formattable::kInt64: return longValueOf(env, res.getInt64());
+ case icu::Formattable::kDouble: return doubleValueOf(env, res.getDouble());
+ case icu::Formattable::kLong: return longValueOf(env, res.getLong());
+ case icu::Formattable::kInt64: return longValueOf(env, res.getInt64());
default: return NULL;
}
}
static jlong NativeDecimalFormat_cloneImpl(JNIEnv*, jclass, jlong addr) {
- DecimalFormat* fmt = toDecimalFormat(addr);
+ icu::DecimalFormat* fmt = toDecimalFormat(addr);
return reinterpret_cast<uintptr_t>(fmt->clone());
}
diff --git a/luni/src/main/native/libcore_icu_NativeIDN.cpp b/luni/src/main/native/libcore_icu_NativeIDN.cpp
index 16a6e1c..43f3ce5 100644
--- a/luni/src/main/native/libcore_icu_NativeIDN.cpp
+++ b/luni/src/main/native/libcore_icu_NativeIDN.cpp
@@ -39,9 +39,18 @@ static jstring NativeIDN_convertImpl(JNIEnv* env, jclass, jstring javaSrc, jint
}
UChar dst[256];
UErrorCode status = U_ZERO_ERROR;
+
+ // We're stuck implementing IDNA-2003 for now since that's what we specify.
+ //
+ // TODO: Change our spec to IDNA-2008 + UTS-46 compatibility processing if
+ // it's safe enough.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
size_t resultLength = toAscii
? uidna_IDNToASCII(src.get(), src.size(), &dst[0], sizeof(dst), flags, NULL, &status)
: uidna_IDNToUnicode(src.get(), src.size(), &dst[0], sizeof(dst), flags, NULL, &status);
+#pragma GCC diagnostic pop
+
if (U_FAILURE(status)) {
jniThrowException(env, "java/lang/IllegalArgumentException", u_errorName(status));
return NULL;
diff --git a/luni/src/main/native/libcore_icu_NativeNormalizer.cpp b/luni/src/main/native/libcore_icu_NativeNormalizer.cpp
index 8ae42d9..2d5e282 100644
--- a/luni/src/main/native/libcore_icu_NativeNormalizer.cpp
+++ b/luni/src/main/native/libcore_icu_NativeNormalizer.cpp
@@ -30,8 +30,8 @@ static jstring NativeNormalizer_normalizeImpl(JNIEnv* env, jclass, jstring s, ji
}
UNormalizationMode mode = static_cast<UNormalizationMode>(intMode);
UErrorCode status = U_ZERO_ERROR;
- UnicodeString dst;
- Normalizer::normalize(src.unicodeString(), mode, 0, dst, status);
+ icu::UnicodeString dst;
+ icu::Normalizer::normalize(src.unicodeString(), mode, 0, dst, status);
maybeThrowIcuException(env, "Normalizer::normalize", status);
return dst.isBogus() ? NULL : env->NewString(dst.getBuffer(), dst.length());
}
@@ -43,7 +43,7 @@ static jboolean NativeNormalizer_isNormalizedImpl(JNIEnv* env, jclass, jstring s
}
UNormalizationMode mode = static_cast<UNormalizationMode>(intMode);
UErrorCode status = U_ZERO_ERROR;
- UBool result = Normalizer::isNormalized(src.unicodeString(), mode, status);
+ UBool result = icu::Normalizer::isNormalized(src.unicodeString(), mode, status);
maybeThrowIcuException(env, "Normalizer::isNormalized", status);
return result;
}
diff --git a/luni/src/main/native/libcore_icu_NativePluralRules.cpp b/luni/src/main/native/libcore_icu_NativePluralRules.cpp
index 8910a8c..f278485 100644
--- a/luni/src/main/native/libcore_icu_NativePluralRules.cpp
+++ b/luni/src/main/native/libcore_icu_NativePluralRules.cpp
@@ -25,8 +25,8 @@
#include <string>
-static PluralRules* toPluralRules(jlong address) {
- return reinterpret_cast<PluralRules*>(static_cast<uintptr_t>(address));
+static icu::PluralRules* toPluralRules(jlong address) {
+ return reinterpret_cast<icu::PluralRules*>(static_cast<uintptr_t>(address));
}
static void NativePluralRules_finalizeImpl(JNIEnv*, jclass, jlong address) {
@@ -48,15 +48,15 @@ static jlong NativePluralRules_forLocaleImpl(JNIEnv* env, jclass, jstring javaLo
localeName[1] = 'i';
}
- Locale locale = Locale::createFromName(localeName.c_str());
+ icu::Locale locale = icu::Locale::createFromName(localeName.c_str());
UErrorCode status = U_ZERO_ERROR;
- PluralRules* result = PluralRules::forLocale(locale, status);
+ icu::PluralRules* result = icu::PluralRules::forLocale(locale, status);
maybeThrowIcuException(env, "PluralRules::forLocale", status);
return reinterpret_cast<uintptr_t>(result);
}
static jint NativePluralRules_quantityForIntImpl(JNIEnv*, jclass, jlong address, jint value) {
- UnicodeString keyword = toPluralRules(address)->select(value);
+ icu::UnicodeString keyword = toPluralRules(address)->select(value);
if (keyword == "zero") {
return 0;
} else if (keyword == "one") {
diff --git a/luni/src/main/native/libcore_icu_TimeZoneNames.cpp b/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
index a7c9098..d30e7a3 100644
--- a/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
+++ b/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "TimeZoneNames"
+#include <memory>
+
#include "IcuUtilities.h"
#include "JNIHelp.h"
#include "JniConstants.h"
@@ -24,32 +26,31 @@
#include "ScopedJavaUnicodeString.h"
#include "ScopedLocalRef.h"
#include "ScopedUtfChars.h"
-#include "UniquePtr.h"
#include "unicode/calendar.h"
#include "unicode/timezone.h"
#include "unicode/tznames.h"
-static bool isUtc(const UnicodeString& id) {
- static const UnicodeString kEtcUct("Etc/UCT", 7, US_INV);
- static const UnicodeString kEtcUtc("Etc/UTC", 7, US_INV);
- static const UnicodeString kEtcUniversal("Etc/Universal", 13, US_INV);
- static const UnicodeString kEtcZulu("Etc/Zulu", 8, US_INV);
+static bool isUtc(const icu::UnicodeString& id) {
+ static const icu::UnicodeString kEtcUct("Etc/UCT", 7, US_INV);
+ static const icu::UnicodeString kEtcUtc("Etc/UTC", 7, US_INV);
+ static const icu::UnicodeString kEtcUniversal("Etc/Universal", 13, US_INV);
+ static const icu::UnicodeString kEtcZulu("Etc/Zulu", 8, US_INV);
- static const UnicodeString kUct("UCT", 3, US_INV);
- static const UnicodeString kUtc("UTC", 3, US_INV);
- static const UnicodeString kUniversal("Universal", 9, US_INV);
- static const UnicodeString kZulu("Zulu", 4, US_INV);
+ static const icu::UnicodeString kUct("UCT", 3, US_INV);
+ static const icu::UnicodeString kUtc("UTC", 3, US_INV);
+ static const icu::UnicodeString kUniversal("Universal", 9, US_INV);
+ static const icu::UnicodeString kZulu("Zulu", 4, US_INV);
return id == kEtcUct || id == kEtcUtc || id == kEtcUniversal || id == kEtcZulu ||
id == kUct || id == kUtc || id == kUniversal || id == kZulu;
}
-static bool setStringArrayElement(JNIEnv* env, jobjectArray array, int i, const UnicodeString& s) {
+static bool setStringArrayElement(JNIEnv* env, jobjectArray array, int i, const icu::UnicodeString& s) {
// Fill in whatever we got. We don't use the display names if they're "GMT[+-]xx:xx"
// because icu4c doesn't use the up-to-date time zone transition data, so it gets these
// wrong. TimeZone.getDisplayName creates accurate names on demand.
// TODO: investigate whether it's worth doing that work once in the Java wrapper instead of on-demand.
- static const UnicodeString kGmt("GMT", 3, US_INV);
+ static const icu::UnicodeString kGmt("GMT", 3, US_INV);
if (!s.isBogus() && !s.startsWith(kGmt)) {
ScopedLocalRef<jstring> javaString(env, env->NewString(s.getBuffer(), s.length()));
if (javaString.get() == NULL) {
@@ -67,14 +68,14 @@ static void TimeZoneNames_fillZoneStrings(JNIEnv* env, jclass, jstring javaLocal
}
UErrorCode status = U_ZERO_ERROR;
- UniquePtr<TimeZoneNames> names(TimeZoneNames::createInstance(icuLocale.locale(), status));
+ std::unique_ptr<icu::TimeZoneNames> names(icu::TimeZoneNames::createInstance(icuLocale.locale(), status));
if (maybeThrowIcuException(env, "TimeZoneNames::createInstance", status)) {
return;
}
- const UDate now(Calendar::getNow());
+ const UDate now(icu::Calendar::getNow());
- static const UnicodeString kUtc("UTC", 3, US_INV);
+ static const icu::UnicodeString kUtc("UTC", 3, US_INV);
size_t id_count = env->GetArrayLength(result);
for (size_t i = 0; i < id_count; ++i) {
@@ -87,13 +88,13 @@ static void TimeZoneNames_fillZoneStrings(JNIEnv* env, jclass, jstring javaLocal
return;
}
- UnicodeString long_std;
+ icu::UnicodeString long_std;
names->getDisplayName(zone_id.unicodeString(), UTZNM_LONG_STANDARD, now, long_std);
- UnicodeString short_std;
+ icu::UnicodeString short_std;
names->getDisplayName(zone_id.unicodeString(), UTZNM_SHORT_STANDARD, now, short_std);
- UnicodeString long_dst;
+ icu::UnicodeString long_dst;
names->getDisplayName(zone_id.unicodeString(), UTZNM_LONG_DAYLIGHT, now, long_dst);
- UnicodeString short_dst;
+ icu::UnicodeString short_dst;
names->getDisplayName(zone_id.unicodeString(), UTZNM_SHORT_DAYLIGHT, now, short_dst);
if (isUtc(zone_id.unicodeString())) {
@@ -123,7 +124,7 @@ static jstring TimeZoneNames_getExemplarLocation(JNIEnv* env, jclass, jstring ja
}
UErrorCode status = U_ZERO_ERROR;
- UniquePtr<TimeZoneNames> names(TimeZoneNames::createInstance(icuLocale.locale(), status));
+ std::unique_ptr<icu::TimeZoneNames> names(icu::TimeZoneNames::createInstance(icuLocale.locale(), status));
if (maybeThrowIcuException(env, "TimeZoneNames::createInstance", status)) {
return NULL;
}
@@ -133,8 +134,8 @@ static jstring TimeZoneNames_getExemplarLocation(JNIEnv* env, jclass, jstring ja
return NULL;
}
- UnicodeString s;
- const UDate now(Calendar::getNow());
+ icu::UnicodeString s;
+ const UDate now(icu::Calendar::getNow());
names->getDisplayName(tz.unicodeString(), UTZNM_EXEMPLAR_LOCATION, now, s);
return env->NewString(s.getBuffer(), s.length());
}
diff --git a/luni/src/main/native/libcore_icu_Transliterator.cpp b/luni/src/main/native/libcore_icu_Transliterator.cpp
index 0c52053..ae21565 100644
--- a/luni/src/main/native/libcore_icu_Transliterator.cpp
+++ b/luni/src/main/native/libcore_icu_Transliterator.cpp
@@ -23,8 +23,8 @@
#include "ScopedJavaUnicodeString.h"
#include "unicode/translit.h"
-static Transliterator* fromPeer(jlong peer) {
- return reinterpret_cast<Transliterator*>(static_cast<uintptr_t>(peer));
+static icu::Transliterator* fromPeer(jlong peer) {
+ return reinterpret_cast<icu::Transliterator*>(static_cast<uintptr_t>(peer));
}
static jlong Transliterator_create(JNIEnv* env, jclass, jstring javaId) {
@@ -33,7 +33,7 @@ static jlong Transliterator_create(JNIEnv* env, jclass, jstring javaId) {
return 0;
}
UErrorCode status = U_ZERO_ERROR;
- Transliterator* t = Transliterator::createInstance(id.unicodeString(), UTRANS_FORWARD, status);
+ icu::Transliterator* t = icu::Transliterator::createInstance(id.unicodeString(), UTRANS_FORWARD, status);
if (maybeThrowIcuException(env, "Transliterator::createInstance", status)) {
return 0;
}
@@ -46,18 +46,18 @@ static void Transliterator_destroy(JNIEnv*, jclass, jlong peer) {
static jobjectArray Transliterator_getAvailableIDs(JNIEnv* env, jclass) {
UErrorCode status = U_ZERO_ERROR;
- StringEnumeration* e = Transliterator::getAvailableIDs(status);
+ icu::StringEnumeration* e = icu::Transliterator::getAvailableIDs(status);
return fromStringEnumeration(env, status, "Transliterator::getAvailableIDs", e);
}
static jstring Transliterator_transliterate(JNIEnv* env, jclass, jlong peer, jstring javaString) {
- Transliterator* t = fromPeer(peer);
+ icu::Transliterator* t = fromPeer(peer);
ScopedJavaUnicodeString string(env, javaString);
if (!string.valid()) {
return NULL;
}
- UnicodeString& s(string.unicodeString());
+ icu::UnicodeString& s(string.unicodeString());
t->transliterate(s);
return env->NewString(s.getBuffer(), s.length());
}
diff --git a/luni/src/main/native/libcore_io_Memory.cpp b/luni/src/main/native/libcore_io_Memory.cpp
index 9edbfb8..5122a6c 100644
--- a/luni/src/main/native/libcore_io_Memory.cpp
+++ b/luni/src/main/native/libcore_io_Memory.cpp
@@ -21,32 +21,12 @@
#include "Portability.h"
#include "ScopedBytes.h"
#include "ScopedPrimitiveArray.h"
-#include "UniquePtr.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
-#if defined(__arm__)
-// 32-bit ARM has load/store alignment restrictions for longs.
-#define LONG_ALIGNMENT_MASK 0x3
-#define INT_ALIGNMENT_MASK 0x0
-#define SHORT_ALIGNMENT_MASK 0x0
-#elif defined(__mips__)
-// MIPS has load/store alignment restrictions for longs, ints and shorts.
-#define LONG_ALIGNMENT_MASK 0x7
-#define INT_ALIGNMENT_MASK 0x3
-#define SHORT_ALIGNMENT_MASK 0x1
-#elif defined(__aarch64__) || defined(__i386__) || defined(__x86_64__)
-// These architectures can load anything at any alignment.
-#define LONG_ALIGNMENT_MASK 0x0
-#define INT_ALIGNMENT_MASK 0x0
-#define SHORT_ALIGNMENT_MASK 0x0
-#else
-#error unknown load/store alignment restrictions for this architecture
-#endif
-
// Use packed structures for access to unaligned data on targets with alignment restrictions.
// The compiler will generate appropriate code to access these structures without
// generating alignment exceptions.
@@ -82,63 +62,31 @@ static inline void swapShorts(jshort* dstShorts, const jshort* srcShorts, size_t
// Do 32-bit swaps as long as possible...
jint* dst = reinterpret_cast<jint*>(dstShorts);
const jint* src = reinterpret_cast<const jint*>(srcShorts);
-
- if ((reinterpret_cast<uintptr_t>(dst) & INT_ALIGNMENT_MASK) == 0 &&
- (reinterpret_cast<uintptr_t>(src) & INT_ALIGNMENT_MASK) == 0) {
- for (size_t i = 0; i < count / 2; ++i) {
- jint v = *src++;
- *dst++ = bswap_2x16(v);
- }
- // ...with one last 16-bit swap if necessary.
- if ((count % 2) != 0) {
- jshort v = *reinterpret_cast<const jshort*>(src);
- *reinterpret_cast<jshort*>(dst) = bswap_16(v);
- }
- } else {
- for (size_t i = 0; i < count / 2; ++i) {
- jint v = get_unaligned<jint>(src++);
- put_unaligned<jint>(dst++, bswap_2x16(v));
- }
- if ((count % 2) != 0) {
- jshort v = get_unaligned<jshort>(reinterpret_cast<const jshort*>(src));
- put_unaligned<jshort>(reinterpret_cast<jshort*>(dst), bswap_16(v));
- }
+ for (size_t i = 0; i < count / 2; ++i) {
+ jint v = get_unaligned<jint>(src++);
+ put_unaligned<jint>(dst++, bswap_2x16(v));
+ }
+ if ((count % 2) != 0) {
+ jshort v = get_unaligned<jshort>(reinterpret_cast<const jshort*>(src));
+ put_unaligned<jshort>(reinterpret_cast<jshort*>(dst), bswap_16(v));
}
}
static inline void swapInts(jint* dstInts, const jint* srcInts, size_t count) {
- if ((reinterpret_cast<uintptr_t>(dstInts) & INT_ALIGNMENT_MASK) == 0 &&
- (reinterpret_cast<uintptr_t>(srcInts) & INT_ALIGNMENT_MASK) == 0) {
- for (size_t i = 0; i < count; ++i) {
- jint v = *srcInts++;
- *dstInts++ = bswap_32(v);
- }
- } else {
- for (size_t i = 0; i < count; ++i) {
- jint v = get_unaligned<int>(srcInts++);
- put_unaligned<jint>(dstInts++, bswap_32(v));
- }
+ for (size_t i = 0; i < count; ++i) {
+ jint v = get_unaligned<int>(srcInts++);
+ put_unaligned<jint>(dstInts++, bswap_32(v));
}
}
static inline void swapLongs(jlong* dstLongs, const jlong* srcLongs, size_t count) {
jint* dst = reinterpret_cast<jint*>(dstLongs);
const jint* src = reinterpret_cast<const jint*>(srcLongs);
- if ((reinterpret_cast<uintptr_t>(dstLongs) & INT_ALIGNMENT_MASK) == 0 &&
- (reinterpret_cast<uintptr_t>(srcLongs) & INT_ALIGNMENT_MASK) == 0) {
- for (size_t i = 0; i < count; ++i) {
- jint v1 = *src++;
- jint v2 = *src++;
- *dst++ = bswap_32(v2);
- *dst++ = bswap_32(v1);
- }
- } else {
- for (size_t i = 0; i < count; ++i) {
- jint v1 = get_unaligned<jint>(src++);
- jint v2 = get_unaligned<jint>(src++);
- put_unaligned<jint>(dst++, bswap_32(v2));
- put_unaligned<jint>(dst++, bswap_32(v1));
- }
+ for (size_t i = 0; i < count; ++i) {
+ jint v1 = get_unaligned<jint>(src++);
+ jint v2 = get_unaligned<jint>(src++);
+ put_unaligned<jint>(dst++, bswap_32(v2));
+ put_unaligned<jint>(dst++, bswap_32(v1));
}
}
@@ -260,39 +208,27 @@ static void Memory_pokeShortArray(JNIEnv* env, jclass, jlong dstAddress, jshortA
}
static jshort Memory_peekShortNative(JNIEnv*, jclass, jlong srcAddress) {
- return *cast<const jshort*>(srcAddress);
+ return get_unaligned<jshort>(cast<const jshort*>(srcAddress));
}
static void Memory_pokeShortNative(JNIEnv*, jclass, jlong dstAddress, jshort value) {
- *cast<jshort*>(dstAddress) = value;
+ put_unaligned<jshort>(cast<jshort*>(dstAddress), value);
}
static jint Memory_peekIntNative(JNIEnv*, jclass, jlong srcAddress) {
- return *cast<const jint*>(srcAddress);
+ return get_unaligned<jint>(cast<const jint*>(srcAddress));
}
static void Memory_pokeIntNative(JNIEnv*, jclass, jlong dstAddress, jint value) {
- *cast<jint*>(dstAddress) = value;
+ put_unaligned<jint>(cast<jint*>(dstAddress), value);
}
static jlong Memory_peekLongNative(JNIEnv*, jclass, jlong srcAddress) {
- jlong result;
- const jlong* src = cast<const jlong*>(srcAddress);
- if ((srcAddress & LONG_ALIGNMENT_MASK) == 0) {
- result = *src;
- } else {
- result = get_unaligned<jlong>(src);
- }
- return result;
+ return get_unaligned<jlong>(cast<const jlong*>(srcAddress));
}
static void Memory_pokeLongNative(JNIEnv*, jclass, jlong dstAddress, jlong value) {
- jlong* dst = cast<jlong*>(dstAddress);
- if ((dstAddress & LONG_ALIGNMENT_MASK) == 0) {
- *dst = value;
- } else {
- put_unaligned<jlong>(dst, value);
- }
+ put_unaligned<jlong>(cast<jlong*>(dstAddress), value);
}
static void unsafeBulkCopy(jbyte* dst, const jbyte* src, jint byteCount,
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index 7e9b22e..f6af483 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -25,20 +25,20 @@
#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"
#include "ScopedUtfChars.h"
#include "toStringArray.h"
-#include "UniquePtr.h"
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
+#include <linux/rtnetlink.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
+#include <netpacket/packet.h>
#include <poll.h>
#include <pwd.h>
#include <signal.h>
@@ -61,7 +61,7 @@
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
-
+#include <memory>
#ifndef __unused
#define __unused __attribute__((__unused__))
@@ -79,6 +79,52 @@ struct addrinfo_deleter {
}
};
+static bool isIPv4MappedAddress(const sockaddr *sa) {
+ const sockaddr_in6 *sin6 = reinterpret_cast<const sockaddr_in6*>(sa);
+ return sa != NULL && sa->sa_family == AF_INET6 &&
+ (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)); // We map 0.0.0.0 to ::, so :: is mapped.
+}
+
+/**
+ * Perform a socket operation that specifies an IP address, possibly falling back from specifying
+ * the address as an IPv4-mapped IPv6 address in a struct sockaddr_in6 to specifying it as an IPv4
+ * address in a struct sockaddr_in.
+ *
+ * This is needed because all sockets created by the java.net APIs are IPv6 sockets, and on those
+ * sockets, IPv4 operations use IPv4-mapped addresses stored in a struct sockaddr_in6. But sockets
+ * created using Posix.socket(AF_INET, ...) are IPv4 sockets and only support operations using IPv4
+ * socket addresses structures.
+ */
+#define NET_IPV4_FALLBACK(jni_env, return_type, syscall_name, java_fd, java_addr, port, null_addr_ok, args...) ({ \
+ return_type _rc = -1; \
+ do { \
+ sockaddr_storage _ss; \
+ socklen_t _salen; \
+ if (java_addr == NULL && null_addr_ok) { \
+ /* No IP address specified (e.g., sendto() on a connected socket). */ \
+ _salen = 0; \
+ } else if (!inetAddressToSockaddr(jni_env, java_addr, port, _ss, _salen)) { \
+ /* Invalid socket address, return -1. inetAddressToSockaddr has already thrown. */ \
+ break; \
+ } \
+ sockaddr* _sa = _salen ? reinterpret_cast<sockaddr*>(&_ss) : NULL; \
+ /* inetAddressToSockaddr always returns an IPv6 sockaddr. Assume that java_fd was created \
+ * by Java API calls, which always create IPv6 socket fds, and pass it in as is. */ \
+ _rc = NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ##args, _sa, _salen); \
+ if (_rc == -1 && errno == EAFNOSUPPORT && _salen && isIPv4MappedAddress(_sa)) { \
+ /* We passed in an IPv4 address in an IPv6 sockaddr and the kernel told us that we got \
+ * the address family wrong. Pass in the same address in an IPv4 sockaddr. */ \
+ jni_env->ExceptionClear(); \
+ if (!inetAddressToSockaddrVerbatim(jni_env, java_addr, port, _ss, _salen)) { \
+ break; \
+ } \
+ _sa = reinterpret_cast<sockaddr*>(&_ss); \
+ _rc = NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ##args, _sa, _salen); \
+ } \
+ } while (0); \
+ _rc; }) \
+
/**
* Used to retry networking system calls that can be interrupted with a signal. Unlike
* TEMP_FAILURE_RETRY, this also handles the case where
@@ -150,6 +196,9 @@ struct addrinfo_deleter {
} while (_rc == -1); /* && _syscallErrno == EINTR && !_wasSignaled */ \
_rc; })
+#define NULL_ADDR_OK true
+#define NULL_ADDR_FORBIDDEN false
+
static void throwException(JNIEnv* env, jclass exceptionClass, jmethodID ctor3, jmethodID ctor2,
const char* functionName, int error) {
jthrowable cause = NULL;
@@ -268,14 +317,43 @@ private:
};
static jobject makeSocketAddress(JNIEnv* env, const sockaddr_storage& ss) {
- jint port;
- jobject inetAddress = sockaddrToInetAddress(env, ss, &port);
- if (inetAddress == NULL) {
- return NULL;
- }
- static jmethodID ctor = env->GetMethodID(JniConstants::inetSocketAddressClass, "<init>",
- "(Ljava/net/InetAddress;I)V");
- return env->NewObject(JniConstants::inetSocketAddressClass, ctor, inetAddress, port);
+ if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6 || ss.ss_family == AF_UNIX) {
+ jint port;
+ jobject inetAddress = sockaddrToInetAddress(env, ss, &port);
+ if (inetAddress == NULL) {
+ return NULL; // Exception already thrown.
+ }
+ static jmethodID ctor = env->GetMethodID(JniConstants::inetSocketAddressClass,
+ "<init>", "(Ljava/net/InetAddress;I)V");
+ return env->NewObject(JniConstants::inetSocketAddressClass, ctor, inetAddress, port);
+ } else if (ss.ss_family == AF_NETLINK) {
+ const struct sockaddr_nl* nl_addr = reinterpret_cast<const struct sockaddr_nl*>(&ss);
+ static jmethodID ctor = env->GetMethodID(JniConstants::netlinkSocketAddressClass,
+ "<init>", "(II)V");
+ return env->NewObject(JniConstants::netlinkSocketAddressClass, ctor,
+ static_cast<jint>(nl_addr->nl_pid),
+ static_cast<jint>(nl_addr->nl_groups));
+ } else if (ss.ss_family == AF_PACKET) {
+ const struct sockaddr_ll* sll = reinterpret_cast<const struct sockaddr_ll*>(&ss);
+ static jmethodID ctor = env->GetMethodID(JniConstants::packetSocketAddressClass,
+ "<init>", "(SISB[B)V");
+ ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(sll->sll_halen));
+ if (byteArray.get() == NULL) {
+ return NULL;
+ }
+ env->SetByteArrayRegion(byteArray.get(), 0, sll->sll_halen,
+ reinterpret_cast<const jbyte*>(sll->sll_addr));
+ jobject packetSocketAddress = env->NewObject(JniConstants::packetSocketAddressClass, ctor,
+ static_cast<jshort>(ntohs(sll->sll_protocol)),
+ static_cast<jint>(sll->sll_ifindex),
+ static_cast<jshort>(sll->sll_hatype),
+ static_cast<jbyte>(sll->sll_pkttype),
+ byteArray.get());
+ return packetSocketAddress;
+ }
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "unsupported ss_family: %d",
+ ss.ss_family);
+ return NULL;
}
static jobject makeStructPasswd(JNIEnv* env, const struct passwd& pw) {
@@ -386,6 +464,93 @@ static bool fillInetSocketAddress(JNIEnv* env, jint rc, jobject javaInetSocketAd
return true;
}
+static void javaInetSocketAddressToInetAddressAndPort(
+ JNIEnv* env, jobject javaInetSocketAddress, jobject& javaInetAddress, jint& port) {
+ static jfieldID addressFid = env->GetFieldID(
+ JniConstants::inetSocketAddressClass, "addr", "Ljava/net/InetAddress;");
+ static jfieldID portFid = env->GetFieldID(JniConstants::inetSocketAddressClass, "port", "I");
+ javaInetAddress = env->GetObjectField(javaInetSocketAddress, addressFid);
+ port = env->GetIntField(javaInetSocketAddress, portFid);
+}
+
+static bool javaInetSocketAddressToSockaddr(
+ JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) {
+ jobject javaInetAddress;
+ jint port;
+ javaInetSocketAddressToInetAddressAndPort(env, javaSocketAddress, javaInetAddress, port);
+ return inetAddressToSockaddr(env, javaInetAddress, port, ss, sa_len);
+}
+
+static bool javaNetlinkSocketAddressToSockaddr(
+ JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) {
+ static jfieldID nlPidFid = env->GetFieldID(
+ JniConstants::netlinkSocketAddressClass, "nlPortId", "I");
+ static jfieldID nlGroupsFid = env->GetFieldID(
+ JniConstants::netlinkSocketAddressClass, "nlGroupsMask", "I");
+
+ sockaddr_nl *nlAddr = reinterpret_cast<sockaddr_nl *>(&ss);
+ nlAddr->nl_family = AF_NETLINK;
+ nlAddr->nl_pid = env->GetIntField(javaSocketAddress, nlPidFid);
+ nlAddr->nl_groups = env->GetIntField(javaSocketAddress, nlGroupsFid);
+ sa_len = sizeof(sockaddr_nl);
+ return true;
+}
+
+static bool javaPacketSocketAddressToSockaddr(
+ JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) {
+ static jfieldID protocolFid = env->GetFieldID(
+ JniConstants::packetSocketAddressClass, "sll_protocol", "S");
+ static jfieldID ifindexFid = env->GetFieldID(
+ JniConstants::packetSocketAddressClass, "sll_ifindex", "I");
+ static jfieldID hatypeFid = env->GetFieldID(
+ JniConstants::packetSocketAddressClass, "sll_hatype", "S");
+ static jfieldID pkttypeFid = env->GetFieldID(
+ JniConstants::packetSocketAddressClass, "sll_pkttype", "B");
+ static jfieldID addrFid = env->GetFieldID(
+ JniConstants::packetSocketAddressClass, "sll_addr", "[B");
+
+ sockaddr_ll *sll = reinterpret_cast<sockaddr_ll *>(&ss);
+ sll->sll_family = AF_PACKET;
+ sll->sll_protocol = htons(env->GetShortField(javaSocketAddress, protocolFid));
+ sll->sll_ifindex = env->GetIntField(javaSocketAddress, ifindexFid);
+ sll->sll_hatype = env->GetShortField(javaSocketAddress, hatypeFid);
+ sll->sll_pkttype = env->GetByteField(javaSocketAddress, pkttypeFid);
+
+ jbyteArray sllAddr = (jbyteArray) env->GetObjectField(javaSocketAddress, addrFid);
+ if (sllAddr == NULL) {
+ sll->sll_halen = 0;
+ memset(&sll->sll_addr, 0, sizeof(sll->sll_addr));
+ } else {
+ jsize len = env->GetArrayLength(sllAddr);
+ if ((size_t) len > sizeof(sll->sll_addr)) {
+ len = sizeof(sll->sll_addr);
+ }
+ sll->sll_halen = len;
+ env->GetByteArrayRegion(sllAddr, 0, len, (jbyte*) sll->sll_addr);
+ }
+ sa_len = sizeof(sockaddr_ll);
+ return true;
+}
+
+static bool javaSocketAddressToSockaddr(
+ JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) {
+ if (javaSocketAddress == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return false;
+ }
+
+ if (env->IsInstanceOf(javaSocketAddress, JniConstants::netlinkSocketAddressClass)) {
+ return javaNetlinkSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len);
+ } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::inetSocketAddressClass)) {
+ return javaInetSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len);
+ } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::packetSocketAddressClass)) {
+ return javaPacketSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len);
+ }
+ jniThrowException(env, "java/lang/UnsupportedOperationException",
+ "unsupported SocketAddress subclass");
+ return false;
+}
+
static jobject doStat(JNIEnv* env, jstring javaPath, bool isLstat) {
ScopedUtfChars path(env, javaPath);
if (path.c_str() == NULL) {
@@ -446,7 +611,7 @@ private:
}
JNIEnv* mEnv;
- UniquePtr<char[]> mBuffer;
+ std::unique_ptr<char[]> mBuffer;
size_t mBufferSize;
struct passwd mPwd;
struct passwd* mResult;
@@ -479,11 +644,18 @@ static jboolean Posix_access(JNIEnv* env, jobject, jstring javaPath, jint mode)
}
static void Posix_bind(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) {
+ // We don't need the return value because we'll already have thrown.
+ (void) NET_IPV4_FALLBACK(env, int, bind, javaFd, javaAddress, port, NULL_ADDR_FORBIDDEN);
+}
+
+static void Posix_bindSocketAddress(
+ JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) {
sockaddr_storage ss;
socklen_t sa_len;
- if (!inetAddressToSockaddr(env, javaAddress, port, ss, sa_len)) {
- return;
+ if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) {
+ return; // Exception already thrown.
}
+
const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
// We don't need the return value because we'll already have thrown.
(void) NET_FAILURE_RETRY(env, int, bind, javaFd, sa, sa_len);
@@ -518,11 +690,17 @@ static void Posix_close(JNIEnv* env, jobject, jobject javaFd) {
}
static void Posix_connect(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) {
+ (void) NET_IPV4_FALLBACK(env, int, connect, javaFd, javaAddress, port, NULL_ADDR_FORBIDDEN);
+}
+
+static void Posix_connectSocketAddress(
+ JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) {
sockaddr_storage ss;
socklen_t sa_len;
- if (!inetAddressToSockaddr(env, javaAddress, port, ss, sa_len)) {
- return;
+ if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) {
+ return; // Exception already thrown.
}
+
const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
// We don't need the return value because we'll already have thrown.
(void) NET_FAILURE_RETRY(env, int, connect, javaFd, sa, sa_len);
@@ -553,7 +731,7 @@ static void Posix_execve(JNIEnv* env, jobject, jstring javaFilename, jobjectArra
ExecStrings argv(env, javaArgv);
ExecStrings envp(env, javaEnvp);
- execve(path.c_str(), argv.get(), envp.get());
+ TEMP_FAILURE_RETRY(execve(path.c_str(), argv.get(), envp.get()));
throwErrnoException(env, "execve");
}
@@ -565,7 +743,7 @@ static void Posix_execv(JNIEnv* env, jobject, jstring javaFilename, jobjectArray
}
ExecStrings argv(env, javaArgv);
- execv(path.c_str(), argv.get());
+ TEMP_FAILURE_RETRY(execv(path.c_str(), argv.get()));
throwErrnoException(env, "execv");
}
@@ -580,16 +758,6 @@ static void Posix_fchown(JNIEnv* env, jobject, jobject javaFd, jint uid, jint gi
throwIfMinusOne(env, "fchown", TEMP_FAILURE_RETRY(fchown(fd, uid, gid)));
}
-static jint Posix_fcntlVoid(JNIEnv* env, jobject, jobject javaFd, jint cmd) {
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, cmd)));
-}
-
-static jint Posix_fcntlLong(JNIEnv* env, jobject, jobject javaFd, jint cmd, jlong arg) {
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, cmd, arg)));
-}
-
static jint Posix_fcntlFlock(JNIEnv* env, jobject, jobject javaFd, jint cmd, jobject javaFlock) {
static jfieldID typeFid = env->GetFieldID(JniConstants::structFlockClass, "l_type", "S");
static jfieldID whenceFid = env->GetFieldID(JniConstants::structFlockClass, "l_whence", "S");
@@ -616,6 +784,16 @@ static jint Posix_fcntlFlock(JNIEnv* env, jobject, jobject javaFd, jint cmd, job
return rc;
}
+static jint Posix_fcntlInt(JNIEnv* env, jobject, jobject javaFd, jint cmd, jint arg) {
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, cmd, arg)));
+}
+
+static jint Posix_fcntlVoid(JNIEnv* env, jobject, jobject javaFd, jint cmd) {
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, cmd)));
+}
+
static void Posix_fdatasync(JNIEnv* env, jobject, jobject javaFd) {
int fd = jniGetFDFromFileDescriptor(env, javaFd);
throwIfMinusOne(env, "fdatasync", TEMP_FAILURE_RETRY(fdatasync(fd)));
@@ -679,7 +857,7 @@ static jobjectArray Posix_android_getaddrinfo(JNIEnv* env, jobject, jstring java
addrinfo* addressList = NULL;
errno = 0;
int rc = android_getaddrinfofornet(node.c_str(), NULL, &hints, netId, 0, &addressList);
- UniquePtr<addrinfo, addrinfo_deleter> addressListDeleter(addressList);
+ std::unique_ptr<addrinfo, addrinfo_deleter> addressListDeleter(addressList);
if (rc != 0) {
throwGaiException(env, "android_getaddrinfo", rc);
return NULL;
@@ -765,12 +943,16 @@ static jobject Posix_getpeername(JNIEnv* env, jobject, jobject javaFd) {
return doGetSockName(env, javaFd, false);
}
+static jint Posix_getpgid(JNIEnv* env, jobject, jint pid) {
+ return throwIfMinusOne(env, "getpgid", TEMP_FAILURE_RETRY(getpgid(pid)));
+}
+
static jint Posix_getpid(JNIEnv*, jobject) {
- return getpid();
+ return TEMP_FAILURE_RETRY(getpid());
}
static jint Posix_getppid(JNIEnv*, jobject) {
- return getppid();
+ return TEMP_FAILURE_RETRY(getppid());
}
static jobject Posix_getpwnam(JNIEnv* env, jobject, jstring javaName) {
@@ -868,8 +1050,9 @@ static jint Posix_gettid(JNIEnv* env __unused, jobject) {
return 0;
}
return static_cast<jint>(owner);
+#elif defined(__BIONIC__)
+ return TEMP_FAILURE_RETRY(gettid());
#else
- // Neither bionic nor glibc exposes gettid(2).
return syscall(__NR_gettid);
#endif
}
@@ -1036,9 +1219,13 @@ static jobject Posix_open(JNIEnv* env, jobject, jstring javaPath, jint flags, ji
return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL;
}
-static jobjectArray Posix_pipe(JNIEnv* env, jobject) {
+static jobjectArray Posix_pipe2(JNIEnv* env, jobject, jint flags __unused) {
+#ifdef __APPLE__
+ jniThrowException(env, "java/lang/UnsupportedOperationException", "no pipe2 on Mac OS");
+ return NULL;
+#else
int fds[2];
- throwIfMinusOne(env, "pipe", TEMP_FAILURE_RETRY(pipe(&fds[0])));
+ throwIfMinusOne(env, "pipe2", TEMP_FAILURE_RETRY(pipe2(&fds[0], flags)));
jobjectArray result = env->NewObjectArray(2, JniConstants::fileDescriptorClass, NULL);
if (result == NULL) {
return NULL;
@@ -1054,6 +1241,7 @@ static jobjectArray Posix_pipe(JNIEnv* env, jobject) {
}
}
return result;
+#endif
}
static jint Posix_poll(JNIEnv* env, jobject, jobjectArray javaStructs, jint timeoutMs) {
@@ -1063,7 +1251,7 @@ static jint Posix_poll(JNIEnv* env, jobject, jobjectArray javaStructs, jint time
// Turn the Java android.system.StructPollfd[] into a C++ struct pollfd[].
size_t arrayLength = env->GetArrayLength(javaStructs);
- UniquePtr<struct pollfd[]> fds(new struct pollfd[arrayLength]);
+ std::unique_ptr<struct pollfd[]> fds(new struct pollfd[arrayLength]);
memset(fds.get(), 0, sizeof(struct pollfd) * arrayLength);
size_t count = 0; // Some trailing array elements may be irrelevant. (See below.)
for (size_t i = 0; i < arrayLength; ++i) {
@@ -1084,7 +1272,40 @@ static jint Posix_poll(JNIEnv* env, jobject, jobjectArray javaStructs, jint time
for (size_t i = 0; i < count; ++i) {
monitors.push_back(new AsynchronousCloseMonitor(fds[i].fd));
}
- int rc = poll(fds.get(), count, timeoutMs);
+
+ int rc;
+ while (true) {
+ timespec before;
+ clock_gettime(CLOCK_MONOTONIC, &before);
+
+ rc = poll(fds.get(), count, timeoutMs);
+ if (rc >= 0 || errno != EINTR) {
+ break;
+ }
+
+ // We got EINTR. Work out how much of the original timeout is still left.
+ if (timeoutMs > 0) {
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ timespec diff;
+ diff.tv_sec = now.tv_sec - before.tv_sec;
+ diff.tv_nsec = now.tv_nsec - before.tv_nsec;
+ if (diff.tv_nsec < 0) {
+ --diff.tv_sec;
+ diff.tv_nsec += 1000000000;
+ }
+
+ jint diffMs = diff.tv_sec * 1000 + diff.tv_nsec / 1000000;
+ if (diffMs >= timeoutMs) {
+ rc = 0; // We have less than 1ms left anyway, so just time out.
+ break;
+ }
+
+ timeoutMs -= diffMs;
+ }
+ }
+
for (size_t i = 0; i < monitors.size(); ++i) {
delete monitors[i];
}
@@ -1111,7 +1332,8 @@ static void Posix_posix_fallocate(JNIEnv* env, jobject, jobject javaFd __unused,
"fallocate doesn't exist on a Mac");
#else
int fd = jniGetFDFromFileDescriptor(env, javaFd);
- errno = TEMP_FAILURE_RETRY(posix_fallocate64(fd, offset, length));
+ while ((errno = posix_fallocate64(fd, offset, length)) == EINTR) {
+ }
if (errno != 0) {
throwErrnoException(env, "posix_fallocate");
}
@@ -1124,9 +1346,11 @@ static jint Posix_prctl(JNIEnv* env, jobject, jint option __unused, jlong arg2 _
jniThrowException(env, "java/lang/UnsupportedOperationException", "prctl doesn't exist on a Mac");
return 0;
#else
- int result = prctl(static_cast<int>(option),
- static_cast<unsigned long>(arg2), static_cast<unsigned long>(arg3),
- static_cast<unsigned long>(arg4), static_cast<unsigned long>(arg5));
+ int result = TEMP_FAILURE_RETRY(prctl(static_cast<int>(option),
+ static_cast<unsigned long>(arg2),
+ static_cast<unsigned long>(arg3),
+ static_cast<unsigned long>(arg4),
+ static_cast<unsigned long>(arg5)));
return throwIfMinusOne(env, "prctl", result);
#endif
}
@@ -1235,13 +1459,35 @@ static jint Posix_sendtoBytes(JNIEnv* env, jobject, jobject javaFd, jobject java
if (bytes.get() == NULL) {
return -1;
}
+
+ return NET_IPV4_FALLBACK(env, ssize_t, sendto, javaFd, javaInetAddress, port,
+ NULL_ADDR_OK, bytes.get() + byteOffset, byteCount, flags);
+}
+
+static jint Posix_sendtoBytesSocketAddress(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaSocketAddress) {
+ if (env->IsInstanceOf(javaSocketAddress, JniConstants::inetSocketAddressClass)) {
+ // Use the InetAddress version so we get the benefit of NET_IPV4_FALLBACK.
+ jobject javaInetAddress;
+ jint port;
+ javaInetSocketAddressToInetAddressAndPort(env, javaSocketAddress, javaInetAddress, port);
+ return Posix_sendtoBytes(env, NULL, javaFd, javaBytes, byteOffset, byteCount, flags,
+ javaInetAddress, port);
+ }
+
+ ScopedBytesRO bytes(env, javaBytes);
+ if (bytes.get() == NULL) {
+ return -1;
+ }
+
sockaddr_storage ss;
- socklen_t sa_len = 0;
- if (javaInetAddress != NULL && !inetAddressToSockaddr(env, javaInetAddress, port, ss, sa_len)) {
+ socklen_t sa_len;
+ if (!javaSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len)) {
return -1;
}
- const sockaddr* to = (javaInetAddress != NULL) ? reinterpret_cast<const sockaddr*>(&ss) : NULL;
- return NET_FAILURE_RETRY(env, ssize_t, sendto, javaFd, bytes.get() + byteOffset, byteCount, flags, to, sa_len);
+
+ const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
+ // We don't need the return value because we'll already have thrown.
+ return NET_FAILURE_RETRY(env, ssize_t, sendto, javaFd, bytes.get() + byteOffset, byteCount, flags, sa, sa_len);
}
static void Posix_setegid(JNIEnv* env, jobject, jint egid) {
@@ -1268,6 +1514,18 @@ static void Posix_setgid(JNIEnv* env, jobject, jint gid) {
throwIfMinusOne(env, "setgid", TEMP_FAILURE_RETRY(setgid(gid)));
}
+static void Posix_setpgid(JNIEnv* env, jobject, jint pid, int pgid) {
+ throwIfMinusOne(env, "setpgid", TEMP_FAILURE_RETRY(setpgid(pid, pgid)));
+}
+
+static void Posix_setregid(JNIEnv* env, jobject, jint rgid, int egid) {
+ throwIfMinusOne(env, "setregid", TEMP_FAILURE_RETRY(setregid(rgid, egid)));
+}
+
+static void Posix_setreuid(JNIEnv* env, jobject, jint ruid, int euid) {
+ throwIfMinusOne(env, "setreuid", TEMP_FAILURE_RETRY(setreuid(ruid, euid)));
+}
+
static jint Posix_setsid(JNIEnv* env, jobject) {
return throwIfMinusOne(env, "setsid", TEMP_FAILURE_RETRY(setsid()));
}
@@ -1412,6 +1670,9 @@ static void Posix_shutdown(JNIEnv* env, jobject, jobject javaFd, jint how) {
}
static jobject Posix_socket(JNIEnv* env, jobject, jint domain, jint type, jint protocol) {
+ if (domain == AF_PACKET) {
+ protocol = htons(protocol); // Packet sockets specify the protocol in host byte order.
+ }
int fd = throwIfMinusOne(env, "socket", TEMP_FAILURE_RETRY(socket(domain, type, protocol)));
return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL;
}
@@ -1531,15 +1792,20 @@ static jint Posix_writev(JNIEnv* env, jobject, jobject javaFd, jobjectArray buff
return IO_FAILURE_RETRY(env, ssize_t, writev, javaFd, ioVec.get(), ioVec.size());
}
+#define NATIVE_METHOD_OVERLOAD(className, functionName, signature, variant) \
+ { #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName ## variant) }
+
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_OVERLOAD(Posix, bind, "(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V", SocketAddress),
NATIVE_METHOD(Posix, chmod, "(Ljava/lang/String;I)V"),
NATIVE_METHOD(Posix, chown, "(Ljava/lang/String;II)V"),
NATIVE_METHOD(Posix, close, "(Ljava/io/FileDescriptor;)V"),
NATIVE_METHOD(Posix, connect, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"),
+ NATIVE_METHOD_OVERLOAD(Posix, connect, "(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V", SocketAddress),
NATIVE_METHOD(Posix, dup, "(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;"),
NATIVE_METHOD(Posix, dup2, "(Ljava/io/FileDescriptor;I)Ljava/io/FileDescriptor;"),
NATIVE_METHOD(Posix, environ, "()[Ljava/lang/String;"),
@@ -1547,9 +1813,9 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Posix, execve, "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V"),
NATIVE_METHOD(Posix, fchmod, "(Ljava/io/FileDescriptor;I)V"),
NATIVE_METHOD(Posix, fchown, "(Ljava/io/FileDescriptor;II)V"),
- NATIVE_METHOD(Posix, fcntlVoid, "(Ljava/io/FileDescriptor;I)I"),
- NATIVE_METHOD(Posix, fcntlLong, "(Ljava/io/FileDescriptor;IJ)I"),
NATIVE_METHOD(Posix, fcntlFlock, "(Ljava/io/FileDescriptor;ILandroid/system/StructFlock;)I"),
+ NATIVE_METHOD(Posix, fcntlInt, "(Ljava/io/FileDescriptor;II)I"),
+ NATIVE_METHOD(Posix, fcntlVoid, "(Ljava/io/FileDescriptor;I)I"),
NATIVE_METHOD(Posix, fdatasync, "(Ljava/io/FileDescriptor;)V"),
NATIVE_METHOD(Posix, fstat, "(Ljava/io/FileDescriptor;)Landroid/system/StructStat;"),
NATIVE_METHOD(Posix, fstatvfs, "(Ljava/io/FileDescriptor;)Landroid/system/StructStatVfs;"),
@@ -1562,6 +1828,7 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Posix, getenv, "(Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(Posix, getnameinfo, "(Ljava/net/InetAddress;I)Ljava/lang/String;"),
NATIVE_METHOD(Posix, getpeername, "(Ljava/io/FileDescriptor;)Ljava/net/SocketAddress;"),
+ NATIVE_METHOD(Posix, getpgid, "(I)I"),
NATIVE_METHOD(Posix, getpid, "()I"),
NATIVE_METHOD(Posix, getppid, "()I"),
NATIVE_METHOD(Posix, getpwnam, "(Ljava/lang/String;)Landroid/system/StructPasswd;"),
@@ -1595,7 +1862,7 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Posix, munlock, "(JJ)V"),
NATIVE_METHOD(Posix, munmap, "(JJ)V"),
NATIVE_METHOD(Posix, open, "(Ljava/lang/String;II)Ljava/io/FileDescriptor;"),
- NATIVE_METHOD(Posix, pipe, "()[Ljava/io/FileDescriptor;"),
+ NATIVE_METHOD(Posix, pipe2, "(I)[Ljava/io/FileDescriptor;"),
NATIVE_METHOD(Posix, poll, "([Landroid/system/StructPollfd;I)I"),
NATIVE_METHOD(Posix, posix_fallocate, "(Ljava/io/FileDescriptor;JJ)V"),
NATIVE_METHOD(Posix, prctl, "(IJJJJ)I"),
@@ -1609,10 +1876,14 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Posix, rename, "(Ljava/lang/String;Ljava/lang/String;)V"),
NATIVE_METHOD(Posix, sendfile, "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Landroid/util/MutableLong;J)J"),
NATIVE_METHOD(Posix, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetAddress;I)I"),
+ NATIVE_METHOD_OVERLOAD(Posix, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/SocketAddress;)I", SocketAddress),
NATIVE_METHOD(Posix, setegid, "(I)V"),
NATIVE_METHOD(Posix, setenv, "(Ljava/lang/String;Ljava/lang/String;Z)V"),
NATIVE_METHOD(Posix, seteuid, "(I)V"),
NATIVE_METHOD(Posix, setgid, "(I)V"),
+ NATIVE_METHOD(Posix, setpgid, "(II)V"),
+ NATIVE_METHOD(Posix, setregid, "(II)V"),
+ NATIVE_METHOD(Posix, setreuid, "(II)V"),
NATIVE_METHOD(Posix, setsid, "()I"),
NATIVE_METHOD(Posix, setsockoptByte, "(Ljava/io/FileDescriptor;III)V"),
NATIVE_METHOD(Posix, setsockoptIfreq, "(Ljava/io/FileDescriptor;IILjava/lang/String;)V"),
diff --git a/luni/src/main/native/java_nio_charset_Charsets.cpp b/luni/src/main/native/libcore_util_CharsetUtils.cpp
index a49ba22..57c8172 100644
--- a/luni/src/main/native/java_nio_charset_Charsets.cpp
+++ b/luni/src/main/native/libcore_util_CharsetUtils.cpp
@@ -245,6 +245,6 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Charsets, toIsoLatin1Bytes, "([CII)[B"),
NATIVE_METHOD(Charsets, toUtf8Bytes, "([CII)[B"),
};
-void register_java_nio_charset_Charsets(JNIEnv* env) {
- jniRegisterNativeMethods(env, "java/nio/charset/Charsets", gMethods, NELEM(gMethods));
+void register_libcore_util_CharsetUtils(JNIEnv* env) {
+ jniRegisterNativeMethods(env, "libcore/util/CharsetUtils", gMethods, NELEM(gMethods));
}
diff --git a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
index 2ea8806..48defc1 100644
--- a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
+++ b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
@@ -24,11 +24,12 @@
#include "ScopedPrimitiveArray.h"
#include "ScopedStringChars.h"
#include "ScopedUtfChars.h"
-#include "UniquePtr.h"
#include "jni.h"
#include "cutils/log.h"
#include "unicode/unistr.h"
+#include <memory>
+
#include <string.h>
#include <libexpat/expat.h>
@@ -253,7 +254,7 @@ static int hashString(const char* s) {
*/
static InternedString* newInternedString(JNIEnv* env, const char* bytes, int hash) {
// Allocate a new wrapper.
- UniquePtr<InternedString> wrapper(new InternedString);
+ std::unique_ptr<InternedString> wrapper(new InternedString);
if (wrapper.get() == NULL) {
jniThrowOutOfMemoryError(env, NULL);
return NULL;
@@ -439,7 +440,7 @@ static size_t fillBuffer(ParsingContext* parsingContext, const char* utf8, int b
return -1;
}
UErrorCode status = U_ZERO_ERROR;
- UnicodeString utf16(UnicodeString::fromUTF8(StringPiece(utf8, byteCount)));
+ icu::UnicodeString utf16(icu::UnicodeString::fromUTF8(icu::StringPiece(utf8, byteCount)));
return utf16.extract(chars.get(), byteCount, status);
}
@@ -962,7 +963,7 @@ static void notationDecl(void* data, const char* name, const char* /*base*/, con
static jlong ExpatParser_initialize(JNIEnv* env, jobject object, jstring javaEncoding,
jboolean processNamespaces) {
// Allocate parsing context.
- UniquePtr<ParsingContext> context(new ParsingContext(object));
+ std::unique_ptr<ParsingContext> context(new ParsingContext(object));
if (context.get() == NULL) {
jniThrowOutOfMemoryError(env, NULL);
return 0;
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index 079ecd2..a90c683 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -28,7 +28,6 @@ LOCAL_SRC_FILES := \
java_lang_System.cpp \
java_math_NativeBN.cpp \
java_nio_ByteOrder.cpp \
- java_nio_charset_Charsets.cpp \
java_text_Bidi.cpp \
java_util_jar_StrictJarFile.cpp \
java_util_regex_Matcher.cpp \
@@ -38,9 +37,7 @@ LOCAL_SRC_FILES := \
java_util_zip_Deflater.cpp \
java_util_zip_Inflater.cpp \
libcore_icu_AlphabeticIndex.cpp \
- libcore_icu_DateIntervalFormat.cpp \
libcore_icu_ICU.cpp \
- libcore_icu_NativeBreakIterator.cpp \
libcore_icu_NativeCollation.cpp \
libcore_icu_NativeConverter.cpp \
libcore_icu_NativeDecimalFormat.cpp \
@@ -52,18 +49,12 @@ LOCAL_SRC_FILES := \
libcore_io_AsynchronousCloseMonitor.cpp \
libcore_io_Memory.cpp \
libcore_io_Posix.cpp \
+ libcore_util_CharsetUtils.cpp \
org_apache_harmony_xml_ExpatParser.cpp \
readlink.cpp \
sun_misc_Unsafe.cpp \
valueOf.cpp \
-LOCAL_C_INCLUDES += \
- external/icu/icu4c/source/common \
- external/icu/icu4c/source/i18n \
- external/openssl/include \
- external/zlib \
- system/core/include \
-
LOCAL_STATIC_LIBRARIES += \
libfdlibm \
diff --git a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
index 1c1296b..7adad72 100644
--- a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
+++ b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
@@ -16,10 +16,12 @@
package libcore.icu;
-import java.util.Calendar;
-import java.util.Locale;
-import java.util.TimeZone;
-import static libcore.icu.DateIntervalFormat.*;
+import android.icu.util.Calendar;
+import android.icu.util.TimeZone;
+import android.icu.util.ULocale;
+
+import static libcore.icu.DateIntervalFormat.formatDateRange;
+import static libcore.icu.DateUtilsBridge.*;
public class DateIntervalFormatTest extends junit.framework.TestCase {
private static final long MINUTE = 60 * 1000;
@@ -32,7 +34,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
public void test_formatDateInterval() throws Exception {
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
- Calendar c = Calendar.getInstance(tz, Locale.US);
+ Calendar c = Calendar.getInstance(tz, ULocale.US);
c.set(Calendar.MONTH, Calendar.JANUARY);
c.set(Calendar.DAY_OF_MONTH, 19);
c.set(Calendar.HOUR_OF_DAY, 3);
@@ -51,10 +53,10 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
long noonDuration = (8 * 60 + 30) * 60 * 1000 - 15 * 1000;
long midnightDuration = (3 * 60 + 30) * 60 * 1000 + 15 * 1000;
- Locale de_DE = new Locale("de", "DE");
- Locale en_US = new Locale("en", "US");
- Locale es_ES = new Locale("es", "ES");
- Locale es_US = new Locale("es", "US");
+ ULocale de_DE = new ULocale("de", "DE");
+ ULocale en_US = new ULocale("en", "US");
+ ULocale es_ES = new ULocale("es", "ES");
+ ULocale es_US = new ULocale("es", "US");
assertEquals("Monday", formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_WEEKDAY));
assertEquals("January 19", formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR, FORMAT_SHOW_DATE));
@@ -81,11 +83,11 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
assertEquals("1/19/2009 – 2/9/2012", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
assertEquals("19.1.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19.01.2009 - 22.01.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19.01.2009 - 22.04.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("19.01.2009 - 09.02.2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19.01.2009 – 22.01.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19.01.2009 – 22.04.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19.01.2009 – 09.02.2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
- assertEquals("1/19/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
+ assertEquals("19/1/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
assertEquals("19/1/2009–22/1/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
assertEquals("19/1/2009–22/4/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
assertEquals("19/1/2009–9/2/2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE));
@@ -114,60 +116,61 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// The same tests but for de_DE.
- assertEquals("19.-22. Januar 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, 0));
- assertEquals("19.-22. Jan. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("Mo., 19. - Do., 22. Jan. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("Montag, 19. - Donnerstag, 22. Januar 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+ assertEquals("19.–22. Januar 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, 0));
+ assertEquals("19.–22. Jan. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Mo., 19. – Do., 22. Jan. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("Montag, 19. – Donnerstag, 22. Januar 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
- assertEquals("19. Januar - 22. April 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, 0));
- assertEquals("19. Jan. - 22. Apr. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("Mo., 19. Jan. - Mi., 22. Apr. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("Januar-April 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
+ assertEquals("19. Januar – 22. April 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("19. Jan. – 22. Apr. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Mo., 19. Jan. – Mi., 22. Apr. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("Januar–April 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
- assertEquals("19. Jan. 2009 - 9. Feb. 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("Jan. 2009 - Feb. 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
- assertEquals("19. Januar 2009 - 9. Februar 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, 0));
- assertEquals("Montag, 19. Januar 2009 - Donnerstag, 9. Februar 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+ assertEquals("19. Jan. 2009 – 9. Feb. 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("Jan. 2009 – Feb. 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+ assertEquals("19. Januar 2009 – 9. Februar 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("Montag, 19. Januar 2009 – Donnerstag, 9. Februar 2012", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
// The same tests but for es_US.
- assertEquals("19–22 enero 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
- assertEquals("19–22 ene. 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun., 19 ene.–jue., 22 ene. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("lunes, 19 enero–jueves, 22 enero de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+ assertEquals("19–22 de enero de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
+ assertEquals("19 – 22 de ene. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 de ene. – jue., 22 de ene. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
- assertEquals("19 enero–22 abril de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
- assertEquals("19 ene.–22 abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun., 19 ene.–mié., 22 abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("19 de enero–22 de abril de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("19 de ene. – 22 de abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 de ene. – mié., 22 de abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
assertEquals("enero–abril de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
- assertEquals("19 ene. de 2009–9 feb. de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("ene. 2009–feb. 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
- assertEquals("19 enero de 2009–9 febrero de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
- assertEquals("lunes, 19 enero de 2009–jueves, 9 febrero de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+ assertEquals("19 de ene. de 2009 – 9 de feb. de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("ene. de 2009 – feb. de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+
+ assertEquals("19 de enero de 2009–9 de febrero de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
// The same tests but for es_ES.
- assertEquals("19–22 enero 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0));
+ assertEquals("19–22 de enero de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0));
assertEquals("19–22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun., 19 ene.–jue., 22 ene. de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
- assertEquals("lunes, 19 enero–jueves, 22 enero de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
+ assertEquals("lun., 19 ene.–jue., 22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
- assertEquals("19 enero–22 abril de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0));
- assertEquals("19 ene.–22 abr. de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun., 19 ene.–mié., 22 abr. de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+ assertEquals("19 de enero–22 de abril de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0));
+ assertEquals("19 ene.–22 abr. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("lun., 19 ene.–mié., 22 abr. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
assertEquals("enero–abril de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
- assertEquals("19 ene. de 2009–9 feb. de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+ assertEquals("19 ene. 2009–9 feb. 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
assertEquals("ene. 2009–feb. 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
- assertEquals("19 enero de 2009–9 febrero de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0));
- assertEquals("lunes, 19 enero de 2009–jueves, 9 febrero de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
+ assertEquals("19 de enero de 2009–9 de febrero de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0));
+ assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
}
// http://b/8862241 - we should be able to format dates past 2038.
// See also http://code.google.com/p/android/issues/detail?id=13050.
public void test8862241() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
Calendar c = Calendar.getInstance(tz, l);
c.clear();
@@ -181,7 +184,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// http://b/10089890 - we should take the given time zone into account.
public void test10089890() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
TimeZone pacific = TimeZone.getTimeZone("America/Los_Angeles");
int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL | FORMAT_SHOW_TIME | FORMAT_24HOUR;
@@ -203,7 +206,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
int abbr12 = time12 | FORMAT_ABBREV_ALL;
int abbr24 = time24 | FORMAT_ABBREV_ALL;
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
// Full length on-the-hour times.
@@ -230,7 +233,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// http://b/10560853 - when the time is not displayed, an end time 0 ms into the next day is
// considered to belong to the previous day.
public void test10560853_when_time_not_displayed() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
long midnight = 0;
@@ -254,7 +257,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// http://b/10560853 - when the start and end times are otherwise on the same day,
// an end time 0 ms into the next day is considered to belong to the previous day.
public void test10560853_for_single_day_events() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
@@ -266,7 +269,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// The fix for http://b/10560853 didn't work except for the day around the epoch, which was
// all the unit test checked!
public void test_single_day_events_later_than_epoch() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
@@ -284,7 +287,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// The fix for http://b/10560853 didn't work except for UTC, which was
// all the unit test checked!
public void test_single_day_events_not_in_UTC() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone pacific = TimeZone.getTimeZone("America/Los_Angeles");
int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE;
@@ -305,7 +308,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// http://b/10209343 - even if the caller didn't explicitly ask us to include the year,
// we should do so for years other than the current year.
public void test10209343_when_not_this_year() {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_TIME | FORMAT_24HOUR;
@@ -328,7 +331,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// http://b/10209343 - for the current year, we should honor the FORMAT_SHOW_YEAR flags.
public void test10209343_when_this_year() {
// Construct a date in the current year (whenever the test happens to be run).
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
Calendar c = Calendar.getInstance(utc, l);
c.set(Calendar.MONTH, Calendar.FEBRUARY);
@@ -363,7 +366,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// http://b/8467515 - yet another y2k38 bug report.
public void test8467515() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_YEAR | FORMAT_ABBREV_MONTH | FORMAT_ABBREV_WEEKDAY;
long t;
@@ -383,7 +386,7 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
// http://b/12004664
public void test12004664() throws Exception {
TimeZone utc = TimeZone.getTimeZone("UTC");
- Calendar c = Calendar.getInstance(utc, Locale.US);
+ Calendar c = Calendar.getInstance(utc, ULocale.US);
c.clear();
c.set(Calendar.YEAR, 1980);
c.set(Calendar.MONTH, Calendar.FEBRUARY);
@@ -392,26 +395,26 @@ public class DateIntervalFormatTest extends junit.framework.TestCase {
long thisYear = c.getTimeInMillis();
int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_YEAR;
- assertEquals("Sunday, February 10, 1980", formatDateRange(new Locale("en", "US"), utc, thisYear, thisYear, flags));
+ assertEquals("Sunday, February 10, 1980", formatDateRange(new ULocale("en", "US"), utc, thisYear, thisYear, flags));
- // If we supported non-Gregorian calendars, this is what that we'd expect for these locales.
+ // If we supported non-Gregorian calendars, this is what that we'd expect for these ULocales.
// This is really the correct behavior, but since java.util.Calendar currently only supports
// the Gregorian calendar, we want to deliberately force icu4c to agree, otherwise we'd have
// a mix of calendars throughout an app's UI depending on whether Java or native code formatted
// the date.
- // assertEquals("یکشنبه ۲۱ بهمن ۱۳۵۸ ه‍.ش.", formatDateRange(new Locale("fa"), utc, thisYear, thisYear, flags));
- // assertEquals("AP ۱۳۵۸ سلواغه ۲۱, یکشنبه", formatDateRange(new Locale("ps"), utc, thisYear, thisYear, flags));
- // assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 2523", formatDateRange(new Locale("th"), utc, thisYear, thisYear, flags));
+ // assertEquals("یکشنبه ۲۱ بهمن ۱۳۵۸ ه‍.ش.", formatDateRange(new ULocale("fa"), utc, thisYear, thisYear, flags));
+ // assertEquals("AP ۱۳۵۸ سلواغه ۲۱, یکشنبه", formatDateRange(new ULocale("ps"), utc, thisYear, thisYear, flags));
+ // assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 2523", formatDateRange(new ULocale("th"), utc, thisYear, thisYear, flags));
// For now, here are the localized Gregorian strings instead...
- assertEquals("یکشنبه ۱۰ فوریهٔ ۱۹۸۰", formatDateRange(new Locale("fa"), utc, thisYear, thisYear, flags));
- assertEquals("یکشنبه د ۱۹۸۰ د فبروري ۱۰", formatDateRange(new Locale("ps"), utc, thisYear, thisYear, flags));
- assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 1980", formatDateRange(new Locale("th"), utc, thisYear, thisYear, flags));
+ assertEquals("یکشنبه ۱۰ فوریهٔ ۱۹۸۰", formatDateRange(new ULocale("fa"), utc, thisYear, thisYear, flags));
+ assertEquals("یکشنبه د ۱۹۸۰ د فبروري ۱۰", formatDateRange(new ULocale("ps"), utc, thisYear, thisYear, flags));
+ assertEquals("วันอาทิตย์ที่ 10 กุมภาพันธ์ ค.ศ. 1980", formatDateRange(new ULocale("th"), utc, thisYear, thisYear, flags));
}
// http://b/13234532
public void test13234532() throws Exception {
- Locale l = Locale.US;
+ ULocale l = ULocale.US;
TimeZone utc = TimeZone.getTimeZone("UTC");
int flags = FORMAT_SHOW_TIME | FORMAT_ABBREV_ALL | FORMAT_12HOUR;
diff --git a/luni/src/test/java/libcore/icu/ICUTest.java b/luni/src/test/java/libcore/icu/ICUTest.java
index a7cc7a0..99679a7 100644
--- a/luni/src/test/java/libcore/icu/ICUTest.java
+++ b/luni/src/test/java/libcore/icu/ICUTest.java
@@ -20,6 +20,7 @@ import java.text.BreakIterator;
import java.text.Collator;
import java.util.Arrays;
import java.util.Locale;
+import libcore.util.ZoneInfoDB;
public class ICUTest extends junit.framework.TestCase {
public void test_getISOLanguages() throws Exception {
@@ -152,24 +153,24 @@ public class ICUTest extends junit.framework.TestCase {
assertEquals("sr_ME_#Latn", sr_Latn_ME.toString());
assertEquals("Latn", sr_Latn_ME.getScript());
- assertEquals("Српски", sr_Cyrl_BA.getDisplayLanguage(sr_Cyrl_BA));
+ assertEquals("српски", sr_Cyrl_BA.getDisplayLanguage(sr_Cyrl_BA));
assertEquals("Босна и Херцеговина", sr_Cyrl_BA.getDisplayCountry(sr_Cyrl_BA));
- assertEquals("Ћирилица", sr_Cyrl_BA.getDisplayScript(sr_Cyrl_BA));
+ assertEquals("ћирилица", sr_Cyrl_BA.getDisplayScript(sr_Cyrl_BA));
assertEquals("", sr_Cyrl_BA.getDisplayVariant(sr_Cyrl_BA));
- assertEquals("Српски", sr_Cyrl_ME.getDisplayLanguage(sr_Cyrl_ME));
+ assertEquals("српски", sr_Cyrl_ME.getDisplayLanguage(sr_Cyrl_ME));
assertEquals("Црна Гора", sr_Cyrl_ME.getDisplayCountry(sr_Cyrl_ME));
- assertEquals("Ћирилица", sr_Cyrl_ME.getDisplayScript(sr_Cyrl_ME));
+ assertEquals("ћирилица", sr_Cyrl_ME.getDisplayScript(sr_Cyrl_ME));
assertEquals("", sr_Cyrl_ME.getDisplayVariant(sr_Cyrl_ME));
- assertEquals("Srpski", sr_Latn_BA.getDisplayLanguage(sr_Latn_BA));
+ assertEquals("srpski", sr_Latn_BA.getDisplayLanguage(sr_Latn_BA));
assertEquals("Bosna i Hercegovina", sr_Latn_BA.getDisplayCountry(sr_Latn_BA));
- assertEquals("Latinica", sr_Latn_BA.getDisplayScript(sr_Latn_BA));
+ assertEquals("latinica", sr_Latn_BA.getDisplayScript(sr_Latn_BA));
assertEquals("", sr_Latn_BA.getDisplayVariant(sr_Latn_BA));
- assertEquals("Srpski", sr_Latn_ME.getDisplayLanguage(sr_Latn_ME));
+ assertEquals("srpski", sr_Latn_ME.getDisplayLanguage(sr_Latn_ME));
assertEquals("Crna Gora", sr_Latn_ME.getDisplayCountry(sr_Latn_ME));
- assertEquals("Latinica", sr_Latn_ME.getDisplayScript(sr_Latn_ME));
+ assertEquals("latinica", sr_Latn_ME.getDisplayScript(sr_Latn_ME));
assertEquals("", sr_Latn_ME.getDisplayVariant(sr_Latn_ME));
assertEquals("BIH", sr_Cyrl_BA.getISO3Country());
@@ -253,4 +254,11 @@ public class ICUTest extends junit.framework.TestCase {
ICU.setDefaultLocale(initialDefaultLocale);
}
}
+
+ /** Confirms that ICU agrees with the rest of libcore about the version of the TZ data in use. */
+ public void testTimeZoneDataVersion() {
+ String icu4cTzVersion = ICU.getTZDataVersion();
+ String zoneInfoTzVersion = ZoneInfoDB.getInstance().getVersion();
+ assertEquals(icu4cTzVersion, zoneInfoTzVersion);
+ }
}
diff --git a/luni/src/test/java/libcore/icu/LocaleDataTest.java b/luni/src/test/java/libcore/icu/LocaleDataTest.java
index 0a83c53..09c3f0f 100644
--- a/luni/src/test/java/libcore/icu/LocaleDataTest.java
+++ b/luni/src/test/java/libcore/icu/LocaleDataTest.java
@@ -24,7 +24,7 @@ public class LocaleDataTest extends junit.framework.TestCase {
for (Locale l : Locale.getAvailableLocales()) {
LocaleData d = LocaleData.get(l);
// System.err.format("%20s %s %s %s\n", l, d.yesterday, d.today, d.tomorrow);
- // System.err.format("%20s %10s %10s\n", l, d.timeFormat12, d.timeFormat24);
+ // System.err.format("%20s %10s %10s\n", l, d.timeFormat_hm, d.timeFormat_Hm);
}
}
@@ -73,7 +73,7 @@ public class LocaleDataTest extends junit.framework.TestCase {
assertEquals("leden", l.longStandAloneMonthNames[0]);
assertEquals("led", l.shortStandAloneMonthNames[0]);
- assertEquals("l", l.tinyStandAloneMonthNames[0]);
+ assertEquals("1", l.tinyStandAloneMonthNames[0]);
}
public void test_ko_KR() throws Exception {
@@ -124,11 +124,11 @@ public class LocaleDataTest extends junit.framework.TestCase {
// http://b/7924970
public void testTimeFormat12And24() throws Exception {
LocaleData en_US = LocaleData.get(Locale.US);
- assertEquals("h:mm a", en_US.timeFormat12);
- assertEquals("HH:mm", en_US.timeFormat24);
+ assertEquals("h:mm a", en_US.timeFormat_hm);
+ assertEquals("HH:mm", en_US.timeFormat_Hm);
LocaleData ja_JP = LocaleData.get(Locale.JAPAN);
- assertEquals("aK:mm", ja_JP.timeFormat12);
- assertEquals("H:mm", ja_JP.timeFormat24);
+ assertEquals("aK:mm", ja_JP.timeFormat_hm);
+ assertEquals("H:mm", ja_JP.timeFormat_Hm);
}
}
diff --git a/luni/src/test/java/libcore/icu/RelativeDateTimeFormatterTest.java b/luni/src/test/java/libcore/icu/RelativeDateTimeFormatterTest.java
new file mode 100644
index 0000000..101896f
--- /dev/null
+++ b/luni/src/test/java/libcore/icu/RelativeDateTimeFormatterTest.java
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.icu;
+
+import android.icu.util.ULocale;
+
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_ALL;
+import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_RELATIVE;
+import static libcore.icu.DateUtilsBridge.FORMAT_NO_YEAR;
+import static libcore.icu.DateUtilsBridge.FORMAT_NUMERIC_DATE;
+import static libcore.icu.DateUtilsBridge.FORMAT_SHOW_YEAR;
+import static libcore.icu.RelativeDateTimeFormatter.DAY_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.HOUR_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.MINUTE_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.SECOND_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.WEEK_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.YEAR_IN_MILLIS;
+import static libcore.icu.RelativeDateTimeFormatter.getRelativeDateTimeString;
+import static libcore.icu.RelativeDateTimeFormatter.getRelativeTimeSpanString;
+
+public class RelativeDateTimeFormatterTest extends junit.framework.TestCase {
+
+ // Tests adopted from CTS tests for DateUtils.getRelativeTimeSpanString.
+ public void test_getRelativeTimeSpanStringCTS() throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("GMT");
+ Calendar cal = Calendar.getInstance(tz, en_US);
+ // Feb 5, 2015 at 10:50 GMT
+ cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+ final long baseTime = cal.getTimeInMillis();
+
+ assertEquals("0 minutes ago",
+ getRelativeTimeSpanString(en_US, tz, baseTime - SECOND_IN_MILLIS, baseTime,
+ MINUTE_IN_MILLIS, 0));
+ assertEquals("In 0 minutes",
+ getRelativeTimeSpanString(en_US, tz, baseTime + SECOND_IN_MILLIS, baseTime,
+ MINUTE_IN_MILLIS, 0));
+
+ assertEquals("1 minute ago",
+ getRelativeTimeSpanString(en_US, tz, 0, MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 0));
+ assertEquals("In 1 minute",
+ getRelativeTimeSpanString(en_US, tz, MINUTE_IN_MILLIS, 0, MINUTE_IN_MILLIS, 0));
+
+ assertEquals("42 minutes ago",
+ getRelativeTimeSpanString(en_US, tz, baseTime - 42 * MINUTE_IN_MILLIS, baseTime,
+ MINUTE_IN_MILLIS, 0));
+ assertEquals("In 42 minutes",
+ getRelativeTimeSpanString(en_US, tz, baseTime + 42 * MINUTE_IN_MILLIS, baseTime,
+ MINUTE_IN_MILLIS, 0));
+
+ final long TWO_HOURS_IN_MS = 2 * HOUR_IN_MILLIS;
+ assertEquals("2 hours ago",
+ getRelativeTimeSpanString(en_US, tz, baseTime - TWO_HOURS_IN_MS, baseTime,
+ MINUTE_IN_MILLIS, FORMAT_NUMERIC_DATE));
+ assertEquals("In 2 hours",
+ getRelativeTimeSpanString(en_US, tz, baseTime + TWO_HOURS_IN_MS, baseTime,
+ MINUTE_IN_MILLIS, FORMAT_NUMERIC_DATE));
+
+ assertEquals("In 42 min.",
+ getRelativeTimeSpanString(en_US, tz, baseTime + (42 * MINUTE_IN_MILLIS), baseTime,
+ MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+
+ assertEquals("Tomorrow",
+ getRelativeTimeSpanString(en_US, tz, DAY_IN_MILLIS, 0, DAY_IN_MILLIS, 0));
+ assertEquals("In 2 days",
+ getRelativeTimeSpanString(en_US, tz, 2 * DAY_IN_MILLIS, 0, DAY_IN_MILLIS, 0));
+ assertEquals("Yesterday",
+ getRelativeTimeSpanString(en_US, tz, 0, DAY_IN_MILLIS, DAY_IN_MILLIS, 0));
+ assertEquals("2 days ago",
+ getRelativeTimeSpanString(en_US, tz, 0, 2 * DAY_IN_MILLIS, DAY_IN_MILLIS, 0));
+
+ final long DAY_DURATION = 5 * 24 * 60 * 60 * 1000;
+ assertEquals("5 days ago",
+ getRelativeTimeSpanString(en_US, tz, baseTime - DAY_DURATION, baseTime,
+ DAY_IN_MILLIS, 0));
+ }
+
+ private void test_getRelativeTimeSpanString_helper(long delta, long minResolution, int flags,
+ String expectedInPast,
+ String expectedInFuture) throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+ Calendar cal = Calendar.getInstance(tz, en_US);
+ // Feb 5, 2015 at 10:50 PST
+ cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+ final long base = cal.getTimeInMillis();
+
+ assertEquals(expectedInPast,
+ getRelativeTimeSpanString(en_US, tz, base - delta, base, minResolution, flags));
+ assertEquals(expectedInFuture,
+ getRelativeTimeSpanString(en_US, tz, base + delta, base, minResolution, flags));
+ }
+
+ private void test_getRelativeTimeSpanString_helper(long delta, long minResolution,
+ String expectedInPast,
+ String expectedInFuture) throws Exception {
+ test_getRelativeTimeSpanString_helper(delta, minResolution, 0, expectedInPast, expectedInFuture);
+ }
+
+ public void test_getRelativeTimeSpanString() throws Exception {
+
+ test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, 0, "0 seconds ago", "0 seconds ago");
+ test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, "1 minute ago", "In 1 minute");
+ test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, "1 minute ago", "In 1 minute");
+ test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, 0, "5 days ago", "In 5 days");
+
+ test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "0 seconds ago",
+ "0 seconds ago");
+ test_getRelativeTimeSpanString_helper(1 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "1 second ago",
+ "In 1 second");
+ test_getRelativeTimeSpanString_helper(2 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "2 seconds ago",
+ "In 2 seconds");
+ test_getRelativeTimeSpanString_helper(25 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "25 seconds ago",
+ "In 25 seconds");
+ test_getRelativeTimeSpanString_helper(75 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "1 minute ago",
+ "In 1 minute");
+ test_getRelativeTimeSpanString_helper(5000 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "1 hour ago",
+ "In 1 hour");
+
+ test_getRelativeTimeSpanString_helper(0 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "0 minutes ago",
+ "0 minutes ago");
+ test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "1 minute ago",
+ "In 1 minute");
+ test_getRelativeTimeSpanString_helper(2 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "2 minutes ago",
+ "In 2 minutes");
+ test_getRelativeTimeSpanString_helper(25 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "25 minutes ago",
+ "In 25 minutes");
+ test_getRelativeTimeSpanString_helper(75 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "1 hour ago",
+ "In 1 hour");
+ test_getRelativeTimeSpanString_helper(720 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "12 hours ago",
+ "In 12 hours");
+
+ test_getRelativeTimeSpanString_helper(0 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "0 hours ago",
+ "0 hours ago");
+ test_getRelativeTimeSpanString_helper(1 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "1 hour ago",
+ "In 1 hour");
+ test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "2 hours ago",
+ "In 2 hours");
+ test_getRelativeTimeSpanString_helper(5 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "5 hours ago",
+ "In 5 hours");
+ test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "20 hours ago",
+ "In 20 hours");
+
+ test_getRelativeTimeSpanString_helper(0 * DAY_IN_MILLIS, DAY_IN_MILLIS, "Today", "Today");
+ test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+ "Tomorrow");
+ test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+ "Tomorrow");
+ test_getRelativeTimeSpanString_helper(2 * DAY_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
+ "In 2 days");
+ test_getRelativeTimeSpanString_helper(25 * DAY_IN_MILLIS, DAY_IN_MILLIS, "January 11",
+ "March 2");
+
+ test_getRelativeTimeSpanString_helper(0 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "0 weeks ago",
+ "0 weeks ago");
+ test_getRelativeTimeSpanString_helper(1 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "1 week ago",
+ "In 1 week");
+ test_getRelativeTimeSpanString_helper(2 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "2 weeks ago",
+ "In 2 weeks");
+ test_getRelativeTimeSpanString_helper(25 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "25 weeks ago",
+ "In 25 weeks");
+
+ // duration >= minResolution
+ test_getRelativeTimeSpanString_helper(30 * SECOND_IN_MILLIS, 0, "30 seconds ago",
+ "In 30 seconds");
+ test_getRelativeTimeSpanString_helper(30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
+ "30 minutes ago", "In 30 minutes");
+ test_getRelativeTimeSpanString_helper(30 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, "Yesterday",
+ "Tomorrow");
+ test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, "5 days ago",
+ "In 5 days");
+ test_getRelativeTimeSpanString_helper(30 * WEEK_IN_MILLIS, MINUTE_IN_MILLIS, "July 10, 2014",
+ "September 3");
+ test_getRelativeTimeSpanString_helper(5 * 365 * DAY_IN_MILLIS, MINUTE_IN_MILLIS,
+ "February 6, 2010", "February 4, 2020");
+
+ test_getRelativeTimeSpanString_helper(60 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, "1 minute ago",
+ "In 1 minute");
+ test_getRelativeTimeSpanString_helper(120 * SECOND_IN_MILLIS - 1, MINUTE_IN_MILLIS,
+ "1 minute ago", "In 1 minute");
+ test_getRelativeTimeSpanString_helper(60 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, "1 hour ago",
+ "In 1 hour");
+ test_getRelativeTimeSpanString_helper(120 * MINUTE_IN_MILLIS - 1, HOUR_IN_MILLIS, "1 hour ago",
+ "In 1 hour");
+ test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Today", "Today");
+ test_getRelativeTimeSpanString_helper(12 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+ "Today");
+ test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+ "Tomorrow");
+ test_getRelativeTimeSpanString_helper(48 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
+ "In 2 days");
+ test_getRelativeTimeSpanString_helper(45 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
+ "In 2 days");
+ test_getRelativeTimeSpanString_helper(7 * DAY_IN_MILLIS, WEEK_IN_MILLIS, "1 week ago",
+ "In 1 week");
+ test_getRelativeTimeSpanString_helper(14 * DAY_IN_MILLIS - 1, WEEK_IN_MILLIS, "1 week ago",
+ "In 1 week");
+
+ // duration < minResolution
+ test_getRelativeTimeSpanString_helper(59 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, "0 minutes ago",
+ "In 0 minutes");
+ test_getRelativeTimeSpanString_helper(59 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, "0 hours ago",
+ "In 0 hours");
+ test_getRelativeTimeSpanString_helper(HOUR_IN_MILLIS - 1, HOUR_IN_MILLIS, "0 hours ago",
+ "In 0 hours");
+ test_getRelativeTimeSpanString_helper(DAY_IN_MILLIS - 1, DAY_IN_MILLIS, "Yesterday",
+ "Tomorrow");
+ test_getRelativeTimeSpanString_helper(20 * SECOND_IN_MILLIS, WEEK_IN_MILLIS, "0 weeks ago",
+ "In 0 weeks");
+ test_getRelativeTimeSpanString_helper(WEEK_IN_MILLIS - 1, WEEK_IN_MILLIS, "0 weeks ago",
+ "In 0 weeks");
+ }
+
+ public void test_getRelativeTimeSpanStringAbbrev() throws Exception {
+ int flags = FORMAT_ABBREV_RELATIVE;
+
+ test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, 0, flags, "0 sec. ago",
+ "0 sec. ago");
+ test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, flags, "1 min. ago",
+ "In 1 min.");
+ test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, 0, flags, "5 days ago", "In 5 days");
+
+ test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "0 sec. ago", "0 sec. ago");
+ test_getRelativeTimeSpanString_helper(1 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "1 sec. ago", "In 1 sec.");
+ test_getRelativeTimeSpanString_helper(2 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "2 sec. ago", "In 2 sec.");
+ test_getRelativeTimeSpanString_helper(25 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "25 sec. ago", "In 25 sec.");
+ test_getRelativeTimeSpanString_helper(75 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "1 min. ago", "In 1 min.");
+ test_getRelativeTimeSpanString_helper(5000 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+ "1 hr. ago", "In 1 hr.");
+
+ test_getRelativeTimeSpanString_helper(0 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "0 min. ago", "0 min. ago");
+ test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "1 min. ago", "In 1 min.");
+ test_getRelativeTimeSpanString_helper(2 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "2 min. ago", "In 2 min.");
+ test_getRelativeTimeSpanString_helper(25 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "25 min. ago", "In 25 min.");
+ test_getRelativeTimeSpanString_helper(75 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "1 hr. ago", "In 1 hr.");
+ test_getRelativeTimeSpanString_helper(720 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "12 hr. ago", "In 12 hr.");
+
+ test_getRelativeTimeSpanString_helper(0 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "0 hr. ago", "0 hr. ago");
+ test_getRelativeTimeSpanString_helper(1 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "1 hr. ago", "In 1 hr.");
+ test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "2 hr. ago", "In 2 hr.");
+ test_getRelativeTimeSpanString_helper(5 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "5 hr. ago", "In 5 hr.");
+ test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "20 hr. ago", "In 20 hr.");
+
+ test_getRelativeTimeSpanString_helper(0 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags, "Today",
+ "Today");
+ test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "Yesterday", "Tomorrow");
+ test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "Yesterday", "Tomorrow");
+ test_getRelativeTimeSpanString_helper(2 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "2 days ago", "In 2 days");
+ test_getRelativeTimeSpanString_helper(25 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "January 11", "March 2");
+
+ test_getRelativeTimeSpanString_helper(0 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "0 wk. ago", "0 wk. ago");
+ test_getRelativeTimeSpanString_helper(1 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "1 wk. ago", "In 1 wk.");
+ test_getRelativeTimeSpanString_helper(2 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "2 wk. ago", "In 2 wk.");
+ test_getRelativeTimeSpanString_helper(25 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "25 wk. ago", "In 25 wk.");
+
+ // duration >= minResolution
+ test_getRelativeTimeSpanString_helper(30 * SECOND_IN_MILLIS, 0, flags, "30 sec. ago",
+ "In 30 sec.");
+ test_getRelativeTimeSpanString_helper(30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "30 min. ago", "In 30 min.");
+ test_getRelativeTimeSpanString_helper(30 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "Yesterday", "Tomorrow");
+ test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "5 days ago", "In 5 days");
+ test_getRelativeTimeSpanString_helper(30 * WEEK_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "July 10, 2014", "September 3");
+ test_getRelativeTimeSpanString_helper(5 * 365 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "February 6, 2010", "February 4, 2020");
+
+ test_getRelativeTimeSpanString_helper(60 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "1 min. ago", "In 1 min.");
+ test_getRelativeTimeSpanString_helper(120 * SECOND_IN_MILLIS - 1, MINUTE_IN_MILLIS, flags,
+ "1 min. ago", "In 1 min.");
+ test_getRelativeTimeSpanString_helper(60 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "1 hr. ago", "In 1 hr.");
+ test_getRelativeTimeSpanString_helper(120 * MINUTE_IN_MILLIS - 1, HOUR_IN_MILLIS, flags,
+ "1 hr. ago", "In 1 hr.");
+ test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags, "Today",
+ "Today");
+ test_getRelativeTimeSpanString_helper(12 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "Yesterday", "Today");
+ test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "Yesterday", "Tomorrow");
+ test_getRelativeTimeSpanString_helper(48 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "2 days ago", "In 2 days");
+ test_getRelativeTimeSpanString_helper(45 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+ "2 days ago", "In 2 days");
+ test_getRelativeTimeSpanString_helper(7 * DAY_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "1 wk. ago", "In 1 wk.");
+ test_getRelativeTimeSpanString_helper(14 * DAY_IN_MILLIS - 1, WEEK_IN_MILLIS, flags,
+ "1 wk. ago", "In 1 wk.");
+
+ // duration < minResolution
+ test_getRelativeTimeSpanString_helper(59 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+ "0 min. ago", "In 0 min.");
+ test_getRelativeTimeSpanString_helper(59 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, flags,
+ "0 hr. ago", "In 0 hr.");
+ test_getRelativeTimeSpanString_helper(HOUR_IN_MILLIS - 1, HOUR_IN_MILLIS, flags,
+ "0 hr. ago", "In 0 hr.");
+ test_getRelativeTimeSpanString_helper(DAY_IN_MILLIS - 1, DAY_IN_MILLIS, flags,
+ "Yesterday", "Tomorrow");
+ test_getRelativeTimeSpanString_helper(20 * SECOND_IN_MILLIS, WEEK_IN_MILLIS, flags,
+ "0 wk. ago", "In 0 wk.");
+ test_getRelativeTimeSpanString_helper(WEEK_IN_MILLIS - 1, WEEK_IN_MILLIS, flags,
+ "0 wk. ago", "In 0 wk.");
+
+ }
+
+ public void test_getRelativeTimeSpanStringGerman() throws Exception {
+ // Bug: 19744876
+ // We need to specify the timezone and the time explicitly. Otherwise it
+ // may not always give a correct answer of "tomorrow" by using
+ // (now + DAY_IN_MILLIS).
+ Locale de_DE = new Locale("de", "DE");
+ TimeZone tz = TimeZone.getTimeZone("Europe/Berlin");
+ Calendar cal = Calendar.getInstance(tz, de_DE);
+ // Feb 5, 2015 at 10:50 CET
+ cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+ final long now = cal.getTimeInMillis();
+
+ // 42 minutes ago
+ assertEquals("Vor 42 Minuten", getRelativeTimeSpanString(de_DE, tz,
+ now - 42 * MINUTE_IN_MILLIS, now, MINUTE_IN_MILLIS, 0));
+ // In 42 minutes
+ assertEquals("In 42 Minuten", getRelativeTimeSpanString(de_DE, tz,
+ now + 42 * MINUTE_IN_MILLIS, now, MINUTE_IN_MILLIS, 0));
+ // Yesterday
+ assertEquals("Gestern", getRelativeTimeSpanString(de_DE, tz,
+ now - DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // The day before yesterday
+ assertEquals("Vorgestern", getRelativeTimeSpanString(de_DE, tz,
+ now - 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // Tomorrow
+ assertEquals("Morgen", getRelativeTimeSpanString(de_DE, tz,
+ now + DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // The day after tomorrow
+ assertEquals("Übermorgen", getRelativeTimeSpanString(de_DE, tz,
+ now + 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ }
+
+ public void test_getRelativeTimeSpanStringFrench() throws Exception {
+ Locale fr_FR = new Locale("fr", "FR");
+ TimeZone tz = TimeZone.getTimeZone("Europe/Paris");
+ Calendar cal = Calendar.getInstance(tz, fr_FR);
+ // Feb 5, 2015 at 10:50 CET
+ cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+ final long now = cal.getTimeInMillis();
+
+ // 42 minutes ago
+ assertEquals("Il y a 42 minutes", getRelativeTimeSpanString(fr_FR, tz,
+ now - (42 * MINUTE_IN_MILLIS), now, MINUTE_IN_MILLIS, 0));
+ // In 42 minutes
+ assertEquals("Dans 42 minutes", getRelativeTimeSpanString(fr_FR, tz,
+ now + (42 * MINUTE_IN_MILLIS), now, MINUTE_IN_MILLIS, 0));
+ // Yesterday
+ assertEquals("Hier", getRelativeTimeSpanString(fr_FR, tz,
+ now - DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // The day before yesterday
+ assertEquals("Avant-hier", getRelativeTimeSpanString(fr_FR, tz,
+ now - 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // Tomorrow
+ assertEquals("Demain", getRelativeTimeSpanString(fr_FR, tz,
+ now + DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ // The day after tomorrow
+ assertEquals("Après-demain", getRelativeTimeSpanString(fr_FR, tz,
+ now + 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+ }
+
+ // Tests adopted from CTS tests for DateUtils.getRelativeDateTimeString.
+ public void test_getRelativeDateTimeStringCTS() throws Exception {
+ Locale en_US = Locale.getDefault();
+ TimeZone tz = TimeZone.getDefault();
+ final long baseTime = System.currentTimeMillis();
+
+ final long DAY_DURATION = 5 * 24 * 60 * 60 * 1000;
+ assertNotNull(getRelativeDateTimeString(en_US, tz, baseTime - DAY_DURATION, baseTime,
+ MINUTE_IN_MILLIS, DAY_IN_MILLIS,
+ FORMAT_NUMERIC_DATE));
+ }
+
+ public void test_getRelativeDateTimeString() throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+ Calendar cal = Calendar.getInstance(tz, en_US);
+ // Feb 5, 2015 at 10:50 PST
+ cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+ final long base = cal.getTimeInMillis();
+
+ assertEquals("5 seconds ago, 10:49 AM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * SECOND_IN_MILLIS, base, 0,
+ MINUTE_IN_MILLIS, 0));
+ assertEquals("5 min. ago, 10:45 AM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * MINUTE_IN_MILLIS, base, 0,
+ HOUR_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("0 hr. ago, 10:45 AM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * MINUTE_IN_MILLIS, base,
+ HOUR_IN_MILLIS, DAY_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("5 hours ago, 5:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * HOUR_IN_MILLIS, base,
+ HOUR_IN_MILLIS, DAY_IN_MILLIS, 0));
+ assertEquals("Yesterday, 7:50 PM",
+ getRelativeDateTimeString(en_US, tz, base - 15 * HOUR_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("5 days ago, 10:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * DAY_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ assertEquals("Jan 29, 10:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 7 * DAY_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ assertEquals("11/27/2014, 10:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ assertEquals("11/27/2014, 10:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+ YEAR_IN_MILLIS, 0));
+
+ // User-supplied flags should be ignored when formatting the date clause.
+ final int FORMAT_SHOW_WEEKDAY = 0x00002;
+ assertEquals("11/27/2014, 10:50 AM",
+ getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS,
+ FORMAT_ABBREV_ALL | FORMAT_SHOW_WEEKDAY));
+ }
+
+ public void test_getRelativeDateTimeStringDST() throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+ Calendar cal = Calendar.getInstance(tz, en_US);
+
+ // DST starts on Mar 9, 2014 at 2:00 AM.
+ // So 5 hours before 3:15 AM should be formatted as 'Yesterday, 9:15 PM'.
+ cal.set(2014, Calendar.MARCH, 9, 3, 15, 0);
+ long base = cal.getTimeInMillis();
+ assertEquals("Yesterday, 9:15 PM",
+ getRelativeDateTimeString(en_US, tz, base - 5 * HOUR_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+
+ // 1 hour after 2:00 AM should be formatted as 'In 1 hour, 4:00 AM'.
+ cal.set(2014, Calendar.MARCH, 9, 2, 0, 0);
+ base = cal.getTimeInMillis();
+ assertEquals("In 1 hour, 4:00 AM",
+ getRelativeDateTimeString(en_US, tz, base + 1 * HOUR_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+
+ // DST ends on Nov 2, 2014 at 2:00 AM. Clocks are turned backward 1 hour to
+ // 1:00 AM. 8 hours before 5:20 AM should be 'Yesterday, 10:20 PM'.
+ cal.set(2014, Calendar.NOVEMBER, 2, 5, 20, 0);
+ base = cal.getTimeInMillis();
+ assertEquals("Yesterday, 10:20 PM",
+ getRelativeDateTimeString(en_US, tz, base - 8 * HOUR_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+
+ cal.set(2014, Calendar.NOVEMBER, 2, 0, 45, 0);
+ base = cal.getTimeInMillis();
+ // 45 minutes after 0:45 AM should be 'In 45 minutes, 1:30 AM'.
+ assertEquals("In 45 minutes, 1:30 AM",
+ getRelativeDateTimeString(en_US, tz, base + 45 * MINUTE_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ // 45 minutes later, it should be 'In 45 minutes, 1:15 AM'.
+ assertEquals("In 45 minutes, 1:15 AM",
+ getRelativeDateTimeString(en_US, tz, base + 90 * MINUTE_IN_MILLIS,
+ base + 45 * MINUTE_IN_MILLIS, 0, WEEK_IN_MILLIS, 0));
+ // Another 45 minutes later, it should be 'In 45 minutes, 2:00 AM'.
+ assertEquals("In 45 minutes, 2:00 AM",
+ getRelativeDateTimeString(en_US, tz, base + 135 * MINUTE_IN_MILLIS,
+ base + 90 * MINUTE_IN_MILLIS, 0, WEEK_IN_MILLIS, 0));
+ }
+
+
+ public void test_getRelativeDateTimeStringItalian() throws Exception {
+ Locale it_IT = new Locale("it", "IT");
+ TimeZone tz = TimeZone.getTimeZone("Europe/Rome");
+ Calendar cal = Calendar.getInstance(tz, it_IT);
+ // 05 febbraio 2015 20:15
+ cal.set(2015, Calendar.FEBRUARY, 5, 20, 15, 0);
+ final long base = cal.getTimeInMillis();
+
+ assertEquals("5 secondi fa, 20:14",
+ getRelativeDateTimeString(it_IT, tz, base - 5 * SECOND_IN_MILLIS, base, 0,
+ MINUTE_IN_MILLIS, 0));
+ assertEquals("5 min. fa, 20:10",
+ getRelativeDateTimeString(it_IT, tz, base - 5 * MINUTE_IN_MILLIS, base, 0,
+ HOUR_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("0 h. fa, 20:10",
+ getRelativeDateTimeString(it_IT, tz, base - 5 * MINUTE_IN_MILLIS, base,
+ HOUR_IN_MILLIS, DAY_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("Ieri, 22:15",
+ getRelativeDateTimeString(it_IT, tz, base - 22 * HOUR_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+ assertEquals("5 giorni fa, 20:15",
+ getRelativeDateTimeString(it_IT, tz, base - 5 * DAY_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ assertEquals("27/11/2014, 20:15",
+ getRelativeDateTimeString(it_IT, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+ WEEK_IN_MILLIS, 0));
+ }
+
+ // http://b/5252772: detect the actual date difference
+ public void test5252772() throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+
+ // Now is Sep 2, 2011, 10:23 AM PDT.
+ Calendar nowCalendar = Calendar.getInstance(tz, en_US);
+ nowCalendar.set(2011, Calendar.SEPTEMBER, 2, 10, 23, 0);
+ final long now = nowCalendar.getTimeInMillis();
+
+ // Sep 1, 2011, 10:24 AM
+ Calendar yesterdayCalendar1 = Calendar.getInstance(tz, en_US);
+ yesterdayCalendar1.set(2011, Calendar.SEPTEMBER, 1, 10, 24, 0);
+ long yesterday1 = yesterdayCalendar1.getTimeInMillis();
+ assertEquals("Yesterday, 10:24 AM",
+ getRelativeDateTimeString(en_US, tz, yesterday1, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Sep 1, 2011, 10:22 AM
+ Calendar yesterdayCalendar2 = Calendar.getInstance(tz, en_US);
+ yesterdayCalendar2.set(2011, Calendar.SEPTEMBER, 1, 10, 22, 0);
+ long yesterday2 = yesterdayCalendar2.getTimeInMillis();
+ assertEquals("Yesterday, 10:22 AM",
+ getRelativeDateTimeString(en_US, tz, yesterday2, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Aug 31, 2011, 10:24 AM
+ Calendar twoDaysAgoCalendar1 = Calendar.getInstance(tz, en_US);
+ twoDaysAgoCalendar1.set(2011, Calendar.AUGUST, 31, 10, 24, 0);
+ long twoDaysAgo1 = twoDaysAgoCalendar1.getTimeInMillis();
+ assertEquals("2 days ago, 10:24 AM",
+ getRelativeDateTimeString(en_US, tz, twoDaysAgo1, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Aug 31, 2011, 10:22 AM
+ Calendar twoDaysAgoCalendar2 = Calendar.getInstance(tz, en_US);
+ twoDaysAgoCalendar2.set(2011, Calendar.AUGUST, 31, 10, 22, 0);
+ long twoDaysAgo2 = twoDaysAgoCalendar2.getTimeInMillis();
+ assertEquals("2 days ago, 10:22 AM",
+ getRelativeDateTimeString(en_US, tz, twoDaysAgo2, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Sep 3, 2011, 10:22 AM
+ Calendar tomorrowCalendar1 = Calendar.getInstance(tz, en_US);
+ tomorrowCalendar1.set(2011, Calendar.SEPTEMBER, 3, 10, 22, 0);
+ long tomorrow1 = tomorrowCalendar1.getTimeInMillis();
+ assertEquals("Tomorrow, 10:22 AM",
+ getRelativeDateTimeString(en_US, tz, tomorrow1, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Sep 3, 2011, 10:24 AM
+ Calendar tomorrowCalendar2 = Calendar.getInstance(tz, en_US);
+ tomorrowCalendar2.set(2011, Calendar.SEPTEMBER, 3, 10, 24, 0);
+ long tomorrow2 = tomorrowCalendar2.getTimeInMillis();
+ assertEquals("Tomorrow, 10:24 AM",
+ getRelativeDateTimeString(en_US, tz, tomorrow2, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Sep 4, 2011, 10:22 AM
+ Calendar twoDaysLaterCalendar1 = Calendar.getInstance(tz, en_US);
+ twoDaysLaterCalendar1.set(2011, Calendar.SEPTEMBER, 4, 10, 22, 0);
+ long twoDaysLater1 = twoDaysLaterCalendar1.getTimeInMillis();
+ assertEquals("In 2 days, 10:22 AM",
+ getRelativeDateTimeString(en_US, tz, twoDaysLater1, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+
+ // Sep 4, 2011, 10:24 AM
+ Calendar twoDaysLaterCalendar2 = Calendar.getInstance(tz, en_US);
+ twoDaysLaterCalendar2.set(2011, Calendar.SEPTEMBER, 4, 10, 24, 0);
+ long twoDaysLater2 = twoDaysLaterCalendar2.getTimeInMillis();
+ assertEquals("In 2 days, 10:24 AM",
+ getRelativeDateTimeString(en_US, tz, twoDaysLater2, now, MINUTE_IN_MILLIS,
+ WEEK_IN_MILLIS, 0));
+ }
+
+ // b/19822016: show / hide the year based on the dates in the arguments.
+ public void test_bug19822016() throws Exception {
+ Locale en_US = new Locale("en", "US");
+ TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+ Calendar cal = Calendar.getInstance(tz, en_US);
+ // Feb 5, 2012 at 10:50 PST
+ cal.set(2012, Calendar.FEBRUARY, 5, 10, 50, 0);
+ long base = cal.getTimeInMillis();
+
+ assertEquals("Feb 5, 5:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 5 * HOUR_IN_MILLIS, base, 0, MINUTE_IN_MILLIS, 0));
+ assertEquals("Jan 29, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 7 * DAY_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+ assertEquals("11/27/2011, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 10 * WEEK_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+
+ assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+ assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+ assertEquals("January 6, 2012", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+ assertEquals("December 7, 2011", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+ assertEquals("December 7, 2011", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+ assertEquals("December 7", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+
+ // Feb 5, 2018 at 10:50 PST
+ cal.set(2018, Calendar.FEBRUARY, 5, 10, 50, 0);
+ base = cal.getTimeInMillis();
+ assertEquals("Feb 5, 5:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 5 * HOUR_IN_MILLIS, base, 0, MINUTE_IN_MILLIS, 0));
+ assertEquals("Jan 29, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 7 * DAY_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+ assertEquals("11/27/2017, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+ base - 10 * WEEK_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+
+ assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+ assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+ assertEquals("January 6, 2018", getRelativeTimeSpanString(en_US, tz,
+ base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+ assertEquals("December 7, 2017", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+ assertEquals("December 7, 2017", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+ assertEquals("December 7", getRelativeTimeSpanString(en_US, tz,
+ base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+ }
+}
diff --git a/luni/src/test/java/libcore/io/OsTest.java b/luni/src/test/java/libcore/io/OsTest.java
index a0d1e5a..e648e8a 100644
--- a/luni/src/test/java/libcore/io/OsTest.java
+++ b/luni/src/test/java/libcore/io/OsTest.java
@@ -16,18 +16,26 @@
package libcore.io;
+import android.system.ErrnoException;
+import android.system.NetlinkSocketAddress;
+import android.system.PacketSocketAddress;
+import android.system.StructTimeval;
import android.system.StructUcred;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.InetUnixAddress;
+import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.Locale;
import junit.framework.TestCase;
import static android.system.OsConstants.*;
@@ -44,6 +52,20 @@ public class OsTest extends TestCase {
s.close();
}
+ public void testFcntlInt() throws Exception {
+ File f = File.createTempFile("OsTest", "tst");
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(f);
+ Libcore.os.fcntlInt(fis.getFD(), F_SETFD, FD_CLOEXEC);
+ int flags = Libcore.os.fcntlVoid(fis.getFD(), F_GETFD);
+ assertTrue((flags & FD_CLOEXEC) != 0);
+ } finally {
+ IoUtils.closeQuietly(fis);
+ f.delete();
+ }
+ }
+
public void testUnixDomainSockets_in_file_system() throws Exception {
String path = System.getProperty("java.io.tmpdir") + "/test_unix_socket";
new File(path).delete();
@@ -196,9 +218,10 @@ public class OsTest extends TestCase {
fis.close();
}
- public void test_byteBufferPositions_sendto_recvfrom() throws Exception {
- final FileDescriptor serverFd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
- Libcore.os.bind(serverFd, InetAddress.getLoopbackAddress(), 0);
+ static void checkByteBufferPositions_sendto_recvfrom(
+ int family, InetAddress loopback) throws Exception {
+ final FileDescriptor serverFd = Libcore.os.socket(family, SOCK_STREAM, 0);
+ Libcore.os.bind(serverFd, loopback, 0);
Libcore.os.listen(serverFd, 5);
InetSocketAddress address = (InetSocketAddress) Libcore.os.getsockname(serverFd);
@@ -232,7 +255,7 @@ public class OsTest extends TestCase {
server.start();
- FileDescriptor clientFd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
+ FileDescriptor clientFd = Libcore.os.socket(family, SOCK_STREAM, 0);
Libcore.os.connect(clientFd, address.getAddress(), address.getPort());
final byte[] bytes = "good bye, cruel black hole with fancy distortion".getBytes(StandardCharsets.US_ASCII);
@@ -254,4 +277,146 @@ public class OsTest extends TestCase {
Libcore.os.close(clientFd);
}
+
+ public void test_NetlinkSocket() throws Exception {
+ FileDescriptor nlSocket = Libcore.os.socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ Libcore.os.bind(nlSocket, new NetlinkSocketAddress());
+ NetlinkSocketAddress address = (NetlinkSocketAddress) Libcore.os.getsockname(nlSocket);
+ assertTrue(address.getPortId() > 0);
+ assertEquals(0, address.getGroupsMask());
+
+ NetlinkSocketAddress nlKernel = new NetlinkSocketAddress();
+ Libcore.os.connect(nlSocket, nlKernel);
+ NetlinkSocketAddress nlPeer = (NetlinkSocketAddress) Libcore.os.getpeername(nlSocket);
+ assertEquals(0, nlPeer.getPortId());
+ assertEquals(0, nlPeer.getGroupsMask());
+ Libcore.os.close(nlSocket);
+ }
+
+ public void test_PacketSocketAddress() throws Exception {
+ NetworkInterface lo = NetworkInterface.getByName("lo");
+ FileDescriptor fd = Libcore.os.socket(AF_PACKET, SOCK_DGRAM, ETH_P_IPV6);
+ PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6, lo.getIndex());
+ Libcore.os.bind(fd, addr);
+
+ PacketSocketAddress bound = (PacketSocketAddress) Libcore.os.getsockname(fd);
+ assertEquals((short) ETH_P_IPV6, bound.sll_protocol); // ETH_P_IPV6 is an int.
+ assertEquals(lo.getIndex(), bound.sll_ifindex);
+ assertEquals(ARPHRD_LOOPBACK, bound.sll_hatype);
+ assertEquals(0, bound.sll_pkttype);
+
+ // The loopback address is ETH_ALEN bytes long and is all zeros.
+ // http://lxr.free-electrons.com/source/drivers/net/loopback.c?v=3.10#L167
+ assertEquals(6, bound.sll_addr.length);
+ for (int i = 0; i < 6; i++) {
+ assertEquals(0, bound.sll_addr[i]);
+ }
+ }
+
+ public void test_byteBufferPositions_sendto_recvfrom_af_inet() throws Exception {
+ checkByteBufferPositions_sendto_recvfrom(AF_INET, InetAddress.getByName("127.0.0.1"));
+ }
+
+ public void test_byteBufferPositions_sendto_recvfrom_af_inet6() throws Exception {
+ checkByteBufferPositions_sendto_recvfrom(AF_INET6, InetAddress.getByName("::1"));
+ }
+
+ private void checkSendToSocketAddress(int family, InetAddress loopback) throws Exception {
+ FileDescriptor recvFd = Libcore.os.socket(family, SOCK_DGRAM, 0);
+ Libcore.os.bind(recvFd, loopback, 0);
+ StructTimeval tv = StructTimeval.fromMillis(20);
+ Libcore.os.setsockoptTimeval(recvFd, SOL_SOCKET, SO_RCVTIMEO, tv);
+
+ InetSocketAddress to = ((InetSocketAddress) Libcore.os.getsockname(recvFd));
+ FileDescriptor sendFd = Libcore.os.socket(family, SOCK_DGRAM, 0);
+ byte[] msg = ("Hello, I'm going to a socket address: " + to.toString()).getBytes("UTF-8");
+ int len = msg.length;
+
+ assertEquals(len, Libcore.os.sendto(sendFd, msg, 0, len, 0, to));
+ byte[] received = new byte[msg.length + 42];
+ InetSocketAddress from = new InetSocketAddress();
+ assertEquals(len, Libcore.os.recvfrom(recvFd, received, 0, received.length, 0, from));
+ assertEquals(loopback, from.getAddress());
+ }
+
+ public void test_sendtoSocketAddress_af_inet() throws Exception {
+ checkSendToSocketAddress(AF_INET, InetAddress.getByName("127.0.0.1"));
+ }
+
+ public void test_sendtoSocketAddress_af_inet6() throws Exception {
+ checkSendToSocketAddress(AF_INET6, InetAddress.getByName("::1"));
+ }
+
+ public void test_socketFamilies() throws Exception {
+ FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
+ Libcore.os.bind(fd, InetAddress.getByName("::"), 0);
+ InetSocketAddress localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
+ assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
+
+ fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
+ Libcore.os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
+ localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
+ assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
+
+ fd = Libcore.os.socket(AF_INET, SOCK_STREAM, 0);
+ Libcore.os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
+ localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
+ assertEquals(Inet4Address.ANY, localSocketAddress.getAddress());
+ try {
+ Libcore.os.bind(fd, InetAddress.getByName("::"), 0);
+ fail("Expected ErrnoException binding IPv4 socket to ::");
+ } catch (ErrnoException expected) {
+ assertEquals("Expected EAFNOSUPPORT binding IPv4 socket to ::", EAFNOSUPPORT, expected.errno);
+ }
+ }
+
+ private static void assertArrayEquals(byte[] expected, byte[] actual) {
+ assertTrue("Expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual),
+ Arrays.equals(expected, actual));
+ }
+
+ private static void checkSocketPing(FileDescriptor fd, InetAddress to, byte[] packet,
+ byte type, byte responseType, boolean useSendto) throws Exception {
+ int len = packet.length;
+ packet[0] = type;
+ if (useSendto) {
+ assertEquals(len, Libcore.os.sendto(fd, packet, 0, len, 0, to, 0));
+ } else {
+ Libcore.os.connect(fd, to, 0);
+ assertEquals(len, Libcore.os.sendto(fd, packet, 0, len, 0, null, 0));
+ }
+
+ int icmpId = ((InetSocketAddress) Libcore.os.getsockname(fd)).getPort();
+ byte[] received = new byte[4096];
+ InetSocketAddress srcAddress = new InetSocketAddress();
+ assertEquals(len, Libcore.os.recvfrom(fd, received, 0, received.length, 0, srcAddress));
+ assertEquals(to, srcAddress.getAddress());
+ assertEquals(responseType, received[0]);
+ assertEquals(received[4], (byte) (icmpId >> 8));
+ assertEquals(received[5], (byte) (icmpId & 0xff));
+
+ received = Arrays.copyOf(received, len);
+ received[0] = (byte) type;
+ received[2] = received[3] = 0; // Checksum.
+ received[4] = received[5] = 0; // ICMP ID.
+ assertArrayEquals(packet, received);
+ }
+
+ public void test_socketPing() throws Exception {
+ final byte ICMP_ECHO = 8, ICMP_ECHOREPLY = 0;
+ final byte ICMPV6_ECHO_REQUEST = (byte) 128, ICMPV6_ECHO_REPLY = (byte) 129;
+ final byte[] packet = ("\000\000\000\000" + // ICMP type, code.
+ "\000\000\000\003" + // ICMP ID (== port), sequence number.
+ "Hello myself").getBytes(StandardCharsets.US_ASCII);
+
+ FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
+ InetAddress ipv6Loopback = InetAddress.getByName("::1");
+ checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, true);
+ checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, false);
+
+ fd = Libcore.os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+ InetAddress ipv4Loopback = InetAddress.getByName("127.0.0.1");
+ checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, true);
+ checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, false);
+ }
}
diff --git a/luni/src/test/java/libcore/java/io/FileDescriptorTest.java b/luni/src/test/java/libcore/java/io/FileDescriptorTest.java
index 39472df..e2780c9 100644
--- a/luni/src/test/java/libcore/java/io/FileDescriptorTest.java
+++ b/luni/src/test/java/libcore/java/io/FileDescriptorTest.java
@@ -30,14 +30,14 @@ public class FileDescriptorTest extends TestCase {
new RandomAccessFile(f, "r").getFD().sync();
}
- public void test_isSocket() throws Exception {
+ public void test_isSocket$() throws Exception {
File f = new File("/dev/null");
FileInputStream fis = new FileInputStream(f);
- assertFalse(fis.getFD().isSocket());
+ assertFalse(fis.getFD().isSocket$());
fis.close();
ServerSocket s = new ServerSocket();
- assertTrue(s.getImpl$().getFD$().isSocket());
+ assertTrue(s.getImpl$().getFD$().isSocket$());
s.close();
}
}
diff --git a/luni/src/test/java/libcore/java/io/FileInputStreamTest.java b/luni/src/test/java/libcore/java/io/FileInputStreamTest.java
index 14950ee..26de11a 100644
--- a/luni/src/test/java/libcore/java/io/FileInputStreamTest.java
+++ b/luni/src/test/java/libcore/java/io/FileInputStreamTest.java
@@ -67,7 +67,7 @@ public final class FileInputStreamTest extends TestCase {
}
public void testSkipInPipes() throws Exception {
- FileDescriptor[] pipe = Libcore.os.pipe();
+ FileDescriptor[] pipe = Libcore.os.pipe2(0);
DataFeeder feeder = new DataFeeder(pipe[1]);
try {
feeder.start();
diff --git a/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java b/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java
index afe49b7..8d99457 100644
--- a/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java
+++ b/luni/src/test/java/libcore/java/io/RandomAccessFileTest.java
@@ -20,6 +20,8 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+
import junit.framework.TestCase;
import libcore.java.lang.ref.FinalizationTester;
@@ -73,6 +75,61 @@ public final class RandomAccessFileTest extends TestCase {
FinalizationTester.induceFinalization();
}
}
+
+ // http://b/19892782
+ public void testCloseRaf_sameChannelReturned() throws Exception {
+ RandomAccessFile raf = new RandomAccessFile(file, "rw");
+
+ FileChannel fileChannelBeforeClosing = raf.getChannel();
+ raf.close();
+ FileChannel fileChannelAfterClosing = raf.getChannel();
+ assertSame(fileChannelBeforeClosing, fileChannelAfterClosing);
+ }
+
+ // http://b/19892782
+ public void testCloseRaf_channelIsClosed() throws Exception {
+ RandomAccessFile raf = new RandomAccessFile(file, "rw");
+
+ FileChannel fileChannelBeforeClosing = raf.getChannel();
+ raf.close();
+ FileChannel fileChannelAfterClosing = raf.getChannel();
+ assertFalse(fileChannelBeforeClosing.isOpen());
+ }
+
+ // http://b/19892782
+ public void testCloseFileChannel_sameChannelReturned() throws Exception {
+ RandomAccessFile raf = new RandomAccessFile(file, "rw");
+
+ FileChannel fileChannelBeforeClosing = raf.getChannel();
+ fileChannelBeforeClosing.close();
+
+ FileChannel fileChannelAfterClosing = raf.getChannel();
+ assertSame(fileChannelBeforeClosing, fileChannelAfterClosing);
+ }
+
+ // http://b/19892782
+ public void testCloseFileChannel_returnedFileChannelIsClosed() throws Exception {
+ RandomAccessFile raf = new RandomAccessFile(file, "rw");
+
+ FileChannel fileChannelBeforeClosing = raf.getChannel();
+ // This should close the Raf, and previous implementations wrongly returned a new
+ // open (but useless) channel in this case.
+ fileChannelBeforeClosing.close();
+ FileChannel fileChannelAfterClosing = raf.getChannel();
+ assertFalse(fileChannelBeforeClosing.isOpen());
+ }
+
+ // http://b/19892782
+ public void testCloseRafBeforeGetChannel_returnChannelWithCloseFdAfterClose() throws Exception {
+ RandomAccessFile raf = new RandomAccessFile(file, "rw");
+ raf.close();
+ try {
+ raf.getChannel().size();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
private void createRandomAccessFile(File file) throws Exception {
// TODO: fix our register maps and remove this otherwise unnecessary
// indirection! (http://b/5412580)
diff --git a/luni/src/test/java/libcore/java/lang/FloatTest.java b/luni/src/test/java/libcore/java/lang/FloatTest.java
index 92e7ae4..c25bd5c 100644
--- a/luni/src/test/java/libcore/java/lang/FloatTest.java
+++ b/luni/src/test/java/libcore/java/lang/FloatTest.java
@@ -121,4 +121,20 @@ public class FloatTest extends junit.framework.TestCase {
}
assertEquals(f1, 0f);
}
+
+ // Float equivalent of testParseLargestSubnormalDoublePrecision. http://b/18087920.
+ public void testParseLargestSubnormalFloatPrecision() {
+ // These are different ways of saying MIN_NORMAL.
+ assertEquals(1.1754943508222875e-38f, Float.parseFloat("1.1754943508222875e-38"));
+ assertEquals(1.1754943508222875e-38f, Float.parseFloat("0.00011754943508222875e-34f"));
+ assertEquals(1.1754943508222875e-38f, Float.parseFloat("00000001.1754943508222875e-38f"));
+ assertEquals(1.1754943508222875e-38f, Float.parseFloat("1.17549435082228750000e-38f"));
+ assertEquals(1.1754943508222875e-38f, Float.parseFloat("1.1754943508222875e-0038f"));
+ assertEquals(-1.1754943508222875e-38f, Float.parseFloat("-1.1754943508222875e-38f"));
+
+ // Extra interesting values suggested as part of http://b/18087920.
+ assertEquals(1.1754944e-38f, Float.parseFloat("11754942807573643E-54"));
+ assertEquals(1.1754944e-38f, Float.parseFloat("11754942807573644E-54"));
+ assertEquals(1.1754944e-38f, Float.parseFloat("11754942807573645E-54"));
+ }
}
diff --git a/luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java b/luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java
index d1704fb..4760c6d 100644..100755
--- a/luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldAndroidMonitorTest.java
@@ -103,7 +103,7 @@ public class OldAndroidMonitorTest extends TestCase {
}
private class Interrupter extends Thread {
- Waiter waiter;
+ private final Waiter waiter;
Interrupter(String name, Waiter waiter) {
super(name);
@@ -119,8 +119,7 @@ public class OldAndroidMonitorTest extends TestCase {
}
}
- void run_inner() {
- waiter.spin = true;
+ private void run_inner() {
// System.out.println("InterruptTest: starting waiter");
waiter.start();
@@ -168,7 +167,7 @@ public class OldAndroidMonitorTest extends TestCase {
private class Waiter extends Thread {
Object interrupterLock = new Object();
- Boolean spin = false;
+ volatile boolean spin = true;
Waiter(String name) {
super(name);
@@ -188,6 +187,7 @@ public class OldAndroidMonitorTest extends TestCase {
while (spin) {
// We're going to get interrupted while we spin.
}
+
if (interrupted()) {
// System.out.println("Waiter done spinning; interrupted.");
} else {
@@ -196,7 +196,7 @@ public class OldAndroidMonitorTest extends TestCase {
}
synchronized (this) {
- Boolean sawEx = false;
+ boolean sawEx = false;
try {
synchronized (interrupterLock) {
@@ -216,7 +216,7 @@ public class OldAndroidMonitorTest extends TestCase {
}
}
synchronized (this) {
- Boolean sawEx = false;
+ boolean sawEx = false;
try {
synchronized (interrupterLock) {
@@ -236,7 +236,7 @@ public class OldAndroidMonitorTest extends TestCase {
}
}
synchronized (this) {
- Boolean sawEx = false;
+ boolean sawEx = false;
try {
synchronized (interrupterLock) {
diff --git a/luni/src/test/java/libcore/java/lang/OldClassTest.java b/luni/src/test/java/libcore/java/lang/OldClassTest.java
index 23a42bd..f5bc787 100644
--- a/luni/src/test/java/libcore/java/lang/OldClassTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldClassTest.java
@@ -40,11 +40,6 @@ import tests.support.resource.Support_Resources;
@SuppressWarnings("deprecation")
public class OldClassTest extends junit.framework.TestCase {
-
- public static final String FILENAME =
- OldClassTest.class.getPackage().getName().replace('.', '/') +
- "/test#.properties";
-
final String packageName = getClass().getPackage().getName();
final String classNameInitError1 = packageName + ".TestClass1";
final String classNameInitError2 = packageName + ".TestClass1B";
diff --git a/luni/src/test/java/libcore/java/lang/PackageTest.java b/luni/src/test/java/libcore/java/lang/PackageTest.java
index 6e274a0..c004e23 100644
--- a/luni/src/test/java/libcore/java/lang/PackageTest.java
+++ b/luni/src/test/java/libcore/java/lang/PackageTest.java
@@ -25,9 +25,10 @@ public final class PackageTest extends TestCase {
private static final List<Package> packages = Arrays.asList(Package.getPackages());
public void test_getAnnotations() throws Exception {
- // Package annotations aren't supported, but pre-ICS we crashed.
- assertEquals(0, getClass().getPackage().getAnnotations().length);
- assertEquals(0, getClass().getPackage().getDeclaredAnnotations().length);
+ // Pre-ICS we crashed. To pass, the package-info and TestPackageAnnotation classes must be
+ // on the classpath.
+ assertEquals(1, getClass().getPackage().getAnnotations().length);
+ assertEquals(1, getClass().getPackage().getDeclaredAnnotations().length);
}
public void testGetPackage() {
diff --git a/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java b/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java
index 9766cef..51aed38 100644
--- a/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java
+++ b/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java
@@ -28,7 +28,7 @@ import static tests.support.Support_Exec.execAndCheckOutput;
public class ProcessBuilderTest extends AbstractResourceLeakageDetectorTestCase {
private static String shell() {
- String deviceSh = "/system/bin/sh";
+ String deviceSh = System.getenv("ANDROID_ROOT") + "/bin/sh";
String desktopSh = "/bin/sh";
return new File(deviceSh).exists() ? deviceSh : desktopSh;
}
diff --git a/luni/src/test/java/libcore/java/lang/TestPackageAnnotation.java b/luni/src/test/java/libcore/java/lang/TestPackageAnnotation.java
new file mode 100644
index 0000000..7626206
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/TestPackageAnnotation.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package libcore.java.lang;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+// Used by PackageTest
+@Target(ElementType.PACKAGE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TestPackageAnnotation {}
diff --git a/luni/src/test/java/libcore/java/lang/package-info.java b/luni/src/test/java/libcore/java/lang/package-info.java
new file mode 100644
index 0000000..d916e9a
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Used by PackageTest
+@TestPackageAnnotation
+package libcore.java.lang; \ No newline at end of file
diff --git a/luni/src/test/java/libcore/java/lang/reflect/FieldTest.java b/luni/src/test/java/libcore/java/lang/reflect/FieldTest.java
index b60d984..75665db 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/FieldTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/FieldTest.java
@@ -17,6 +17,8 @@
package libcore.java.lang.reflect;
import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
import junit.framework.TestCase;
public final class FieldTest extends TestCase {
@@ -46,6 +48,56 @@ public final class FieldTest extends TestCase {
assertFalse(f1.equals(f2));
}
+ // Tests that the "synthetic" modifier is handled correctly.
+ // It's supposed to be present but not shown in toString.
+ public void testSyntheticModifier() throws NoSuchFieldException {
+ Field valuesField = Thread.State.class.getDeclaredField("$VALUES");
+ // Check that this test makes sense.
+ assertTrue(valuesField.isSynthetic());
+ assertEquals(Modifier.SYNTHETIC, valuesField.getModifiers() & Modifier.SYNTHETIC);
+ assertEquals("private static final java.lang.Thread$State[] java.lang.Thread$State.$VALUES",
+ valuesField.toString());
+ }
+
+ // Ensure that the "enum constant" bit is not returned in toString.
+ public void testEnumValueField() throws NoSuchFieldException {
+ Field blockedField = Thread.State.class.getDeclaredField("BLOCKED");
+ assertTrue(Thread.State.class.getDeclaredField("BLOCKED").isEnumConstant());
+ assertEquals("public static final", Modifier.toString(blockedField.getModifiers()));
+ assertEquals(
+ "public static final java.lang.Thread$State java.lang.Thread$State.BLOCKED",
+ blockedField.toString());
+ }
+
+ class ClassWithATransientField {
+ private transient Class<String> transientField = String.class;
+ }
+
+ // Tests that the "transient" modifier is handled correctly.
+ // The underlying constant value for it is the same as for the "varargs" method modifier.
+ // http://b/18488857
+ public void testTransientModifier() throws NoSuchFieldException {
+ Field transientField = ClassWithATransientField.class.getDeclaredField("transientField");
+ // Check that this test makes sense.
+ assertEquals(Modifier.TRANSIENT, transientField.getModifiers() & Modifier.TRANSIENT);
+ assertEquals(
+ "private transient java.lang.Class "
+ + "libcore.java.lang.reflect.FieldTest$ClassWithATransientField"
+ + ".transientField",
+ transientField.toString());
+ }
+
+ public void testToGenericString() throws NoSuchFieldException {
+ Field transientField = ClassWithATransientField.class.getDeclaredField("transientField");
+ // Check that this test makes sense.
+ assertEquals(Modifier.TRANSIENT, transientField.getModifiers() & Modifier.TRANSIENT);
+ assertEquals(
+ "private transient java.lang.Class<java.lang.String> "
+ + "libcore.java.lang.reflect.FieldTest$ClassWithATransientField"
+ + ".transientField",
+ transientField.toGenericString());
+ }
+
static class FieldTestHelper {
public String a;
public Object b;
diff --git a/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java b/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java
index c3a436c..a3f9065 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java
@@ -17,6 +17,7 @@
package libcore.java.lang.reflect;
import java.lang.reflect.Method;
+
import junit.framework.TestCase;
public final class MethodTest extends TestCase {
@@ -197,6 +198,23 @@ public final class MethodTest extends TestCase {
assertEquals( "public java.lang.Process java.lang.Runtime.exec(java.lang.String[])"
+ " throws java.io.IOException",
Runtime.class.getMethod("exec", new Class[] { String[].class }).toString());
+ // http://b/18488857
+ assertEquals(
+ "public int java.lang.String.compareTo(java.lang.Object)",
+ String.class.getMethod("compareTo", Object.class).toString());
+ }
+
+ // Tests that the "varargs" modifier is handled correctly.
+ // The underlying constant value for it is the same as for the "transient" field modifier.
+ // http://b/18488857
+ public void testVarargsModifier() throws NoSuchMethodException {
+ Method stringFormatMethod = String.class.getMethod(
+ "format", new Class[] { String.class, Object[].class });
+ assertTrue(stringFormatMethod.isVarArgs());
+ assertEquals(
+ "public static java.lang.String java.lang.String.format("
+ + "java.lang.String,java.lang.Object[])",
+ stringFormatMethod.toString());
}
public static class MethodTestHelper {
diff --git a/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java b/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java
index 1bde157..0505f2f 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/ModifierTest.java
@@ -100,6 +100,9 @@ public class ModifierTest extends junit.framework.TestCase {
}
public void test_toStringI() {
- assertEquals("public abstract", Modifier.toString(Modifier.PUBLIC | Modifier.ABSTRACT));
+ // Note that it checks that "STRICT" is rendered as "strictfp" (for other modifiers,
+ // the displayed name is the same as the lowercase constant name).
+ assertEquals("public abstract strictfp",
+ Modifier.toString(Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.STRICT));
}
}
diff --git a/luni/src/test/java/libcore/java/net/InetAddressTest.java b/luni/src/test/java/libcore/java/net/InetAddressTest.java
index 4b656cc..8bdcf64 100644
--- a/luni/src/test/java/libcore/java/net/InetAddressTest.java
+++ b/luni/src/test/java/libcore/java/net/InetAddressTest.java
@@ -21,10 +21,14 @@ import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
import libcore.util.SerializationTester;
public class InetAddressTest extends junit.framework.TestCase {
+ private static final byte[] LOOPBACK4_BYTES = new byte[] { 127, 0, 0, 1 };
private static final byte[] LOOPBACK6_BYTES = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
private static final String[] INVALID_IPv4_NUMERIC_ADDRESSES = new String[] {
@@ -72,7 +76,7 @@ public class InetAddressTest extends junit.framework.TestCase {
}
private static Inet6Address localhost6() throws Exception {
- return (Inet6Address) InetAddress.getByAddress("localhost", LOOPBACK6_BYTES);
+ return (Inet6Address) InetAddress.getByAddress("ip6-localhost", LOOPBACK6_BYTES);
}
public void test_parseNumericAddress() throws Exception {
@@ -311,4 +315,84 @@ public class InetAddressTest extends junit.framework.TestCase {
assertEquals(resultStrings[i], result);
}
}
+
+ public void test_getHostNameCaches() throws Exception {
+ InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK6_BYTES);
+ assertEquals("::1", inetAddress.getHostString());
+ assertEquals("ip6-localhost", inetAddress.getHostName());
+ // getHostString() should now be different.
+ assertEquals("ip6-localhost", inetAddress.getHostString());
+ }
+
+ public void test_getByAddress_loopbackIpv4() throws Exception {
+ InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK4_BYTES);
+ assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress);
+ assertTrue(inetAddress.isLoopbackAddress());
+ }
+
+ public void test_getByAddress_loopbackIpv6() throws Exception {
+ InetAddress inetAddress = InetAddress.getByAddress(LOOPBACK6_BYTES);
+ assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+ assertTrue(inetAddress.isLoopbackAddress());
+ }
+
+ public void test_getByName_loopbackIpv4() throws Exception {
+ InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
+ assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress);
+ assertTrue(inetAddress.isLoopbackAddress());
+ }
+
+ public void test_getByName_loopbackIpv6() throws Exception {
+ InetAddress inetAddress = InetAddress.getByName("::1");
+ assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+ assertTrue(inetAddress.isLoopbackAddress());
+ }
+
+ public void test_getAllByName_localhost() throws Exception {
+ InetAddress[] inetAddresses = InetAddress.getAllByName("localhost");
+ assertEquals(1, inetAddresses.length);
+ InetAddress inetAddress = inetAddresses[0];
+ assertEquals(LOOPBACK4_BYTES, "localhost", inetAddress);
+ assertTrue(inetAddress.isLoopbackAddress());
+ }
+
+ public void test_getAllByName_ip6_localhost() throws Exception {
+ InetAddress[] inetAddresses = InetAddress.getAllByName("ip6-localhost");
+ assertEquals(1, inetAddresses.length);
+ InetAddress inetAddress = inetAddresses[0];
+ assertEquals(LOOPBACK6_BYTES, "ip6-localhost", inetAddress);
+ assertTrue(inetAddress.isLoopbackAddress());
+ }
+
+ public void test_getByName_null() throws Exception {
+ InetAddress inetAddress = InetAddress.getByName("::1");
+
+ Set<InetAddress> expectedLoopbackAddresses =
+ createSet(Inet4Address.LOOPBACK, Inet6Address.LOOPBACK);
+ assertTrue(expectedLoopbackAddresses.contains(inetAddress));
+ }
+
+ public void test_getAllByName_null() throws Exception {
+ InetAddress[] inetAddresses = InetAddress.getAllByName(null);
+ assertEquals(2, inetAddresses.length);
+ Set<InetAddress> expectedLoopbackAddresses =
+ createSet(Inet4Address.LOOPBACK, Inet6Address.LOOPBACK);
+ assertEquals(expectedLoopbackAddresses, createSet(inetAddresses));
+ }
+
+ private static void assertEquals(
+ byte[] expectedAddressBytes, String expectedHostname, InetAddress actual) {
+ assertArrayEquals(expectedAddressBytes, actual.getAddress());
+ assertEquals(expectedHostname, actual.getHostName());
+
+ }
+
+ private static void assertArrayEquals(byte[] expected, byte[] actual) {
+ assertTrue("Expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual),
+ Arrays.equals(expected, actual));
+ }
+
+ private static Set<InetAddress> createSet(InetAddress... members) {
+ return new HashSet<InetAddress>(Arrays.asList(members));
+ }
}
diff --git a/luni/src/test/java/libcore/java/net/InetSocketAddressTest.java b/luni/src/test/java/libcore/java/net/InetSocketAddressTest.java
index 3bca8dc..d97c48a 100644
--- a/luni/src/test/java/libcore/java/net/InetSocketAddressTest.java
+++ b/luni/src/test/java/libcore/java/net/InetSocketAddressTest.java
@@ -63,7 +63,7 @@ public class InetSocketAddressTest extends TestCase {
}
InetSocketAddress isa = new InetSocketAddress((InetAddress)null, 80);
- assertEquals("0.0.0.0", isa.getHostName());
+ assertEquals("::", isa.getHostName());
try {
new InetSocketAddress(InetAddress.getByName("localhost"), 65536);
@@ -80,7 +80,7 @@ public class InetSocketAddressTest extends TestCase {
public void test_ConstructorI() {
InetSocketAddress isa = new InetSocketAddress(65535);
- assertEquals("0.0.0.0", isa.getHostName());
+ assertEquals("::", isa.getHostName());
assertEquals(65535, isa.getPort());
try {
@@ -150,6 +150,20 @@ public class InetSocketAddressTest extends TestCase {
assertTrue(hasHostname.isUnresolved());
assertEquals("some host", hasHostname.getHostString());
assertEquals("some host", hasHostname.getHostName());
+
+ InetSocketAddress hasHostnameAndAddress = new InetSocketAddress(
+ InetAddress.getByAddress("some host", new byte[] { 127, 0, 0, 1 }),
+ 1234);
+ assertFalse(hasHostnameAndAddress.isUnresolved());
+ assertEquals("some host", hasHostnameAndAddress.getHostString());
+ assertEquals("some host", hasHostnameAndAddress.getHostName());
+
+ // Using a host name that is actually an IP.
+ InetSocketAddress hostnameIsIp = InetSocketAddress.createUnresolved("127.0.0.1", 1234);
+ assertTrue(hostnameIsIp.isUnresolved());
+ assertEquals("127.0.0.1", hostnameIsIp.getHostString());
+ assertEquals("127.0.0.1", hostnameIsIp.getHostName());
+
// When we don't have a hostname, whether or not we do the reverse lookup is the difference
// between getHostString and getHostName...
InetAddress address = InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 });
@@ -157,4 +171,18 @@ public class InetSocketAddressTest extends TestCase {
assertEquals("127.0.0.1", noHostname.getHostString());
assertEquals("localhost", noHostname.getHostName());
}
+
+ public void test_getHostString_cachingBehavior() throws Exception {
+ InetAddress inetAddress = InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 });
+ InetSocketAddress socketAddress = new InetSocketAddress(inetAddress, 1234);
+ assertEquals("127.0.0.1", socketAddress.getHostString());
+ assertEquals("localhost", socketAddress.getHostName());
+ assertEquals("localhost", socketAddress.getHostString());
+
+ inetAddress = InetAddress.getByName("127.0.0.1");
+ socketAddress = new InetSocketAddress(inetAddress, 1234);
+ assertEquals("127.0.0.1", socketAddress.getHostString());
+ assertEquals("localhost", socketAddress.getHostName());
+ assertEquals("localhost", socketAddress.getHostString());
+ }
}
diff --git a/luni/src/test/java/libcore/java/net/OldSocketTest.java b/luni/src/test/java/libcore/java/net/OldSocketTest.java
index 7973965..ded5802 100644
--- a/luni/src/test/java/libcore/java/net/OldSocketTest.java
+++ b/luni/src/test/java/libcore/java/net/OldSocketTest.java
@@ -37,6 +37,7 @@ import java.net.UnknownHostException;
import java.nio.channels.IllegalBlockingModeException;
import java.nio.channels.SocketChannel;
import java.security.Permission;
+import tests.net.StuckServer;
import tests.support.Support_Configuration;
public class OldSocketTest extends OldSocketTestCase {
@@ -932,25 +933,15 @@ public class OldSocketTest extends OldSocketTestCase {
}
// start by validating the error checks
- int portNumber = 0;
- Socket theSocket = null;
- ServerSocket serverSocket = null;
- SocketAddress theAddress = null;
- SocketAddress nonConnectableAddress = null;
- SocketAddress nonReachableAddress = null;
- SocketAddress invalidType = null;
- // byte[] theBytes = {-1,-1,-1,-1};
- byte[] theBytes = { 0, 0, 0, 0 };
- theAddress = new InetSocketAddress(InetAddress.getLocalHost(),
- portNumber);
- nonConnectableAddress = new InetSocketAddress(InetAddress
- .getByAddress(theBytes), portNumber);
- nonReachableAddress = new InetSocketAddress(InetAddress
- .getByName(Support_Configuration.ResolvedNotExistingHost),
- portNumber);
- invalidType = new mySocketAddress();
+ byte[] theBytes = { 0, 0, 0, 0 };
+ SocketAddress theAddress = new InetSocketAddress(InetAddress.getLocalHost(), 0);
+ SocketAddress nonConnectableAddress = new InetSocketAddress(InetAddress.getByAddress(theBytes), 0);
+ SocketAddress nonReachableAddress = new InetSocketAddress(StuckServer.UNREACHABLE_ADDRESS, 0);
+ SocketAddress invalidType = new mySocketAddress();
+ Socket theSocket = null;
+ ServerSocket serverSocket = null;
try {
theSocket = new Socket();
theSocket.connect(null);
@@ -1165,7 +1156,7 @@ public class OldSocketTest extends OldSocketTestCase {
byte[] theBytes = { 0, 0, 0, 0 };
SocketAddress theAddress = new InetSocketAddress(InetAddress.getLocalHost(), 0);
SocketAddress nonConnectableAddress = new InetSocketAddress(InetAddress.getByAddress(theBytes), 0);
- SocketAddress nonReachableAddress = new InetSocketAddress(InetAddress.getByName(Support_Configuration.ResolvedNotExistingHost), 0);
+ SocketAddress nonReachableAddress = new InetSocketAddress(StuckServer.UNREACHABLE_ADDRESS, 0);
SocketAddress invalidType = new mySocketAddress();
Socket theSocket = null;
diff --git a/luni/src/test/java/libcore/java/net/SocketTest.java b/luni/src/test/java/libcore/java/net/SocketTest.java
index fb09be0..9765a45 100644
--- a/luni/src/test/java/libcore/java/net/SocketTest.java
+++ b/luni/src/test/java/libcore/java/net/SocketTest.java
@@ -31,9 +31,11 @@ import java.net.SocketImpl;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
public class SocketTest extends junit.framework.TestCase {
// See http://b/2980559.
@@ -353,6 +355,37 @@ public class SocketTest extends junit.framework.TestCase {
assertEquals(boundAddress.getPort(), localAddressAfterClose.getPort());
}
+ public void testCloseDuringConnect() throws Exception {
+ final CountDownLatch signal = new CountDownLatch(1);
+
+ final Socket s = new Socket();
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ // This address is reserved for documentation: should never be reachable.
+ InetSocketAddress unreachableIp = new InetSocketAddress("192.0.2.0", 80);
+ // This should never return.
+ s.connect(unreachableIp, 0 /* infinite */);
+ fail("Connect returned unexpectedly for: " + unreachableIp);
+ } catch (SocketException expected) {
+ assertTrue(expected.getMessage().contains("Socket closed"));
+ signal.countDown();
+ } catch (IOException e) {
+ fail("Unexpected exception: " + e);
+ }
+ }
+ }.start();
+
+ // Wait for the connect() thread to run and start connect()
+ Thread.sleep(2000);
+
+ s.close();
+
+ boolean connectUnblocked = signal.await(2000, TimeUnit.MILLISECONDS);
+ assertTrue(connectUnblocked);
+ }
+
static class MockServer {
private ExecutorService executor;
private ServerSocket serverSocket;
diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
index c09939f..3f831e0 100644
--- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
@@ -16,7 +16,8 @@
package libcore.java.net;
-import com.android.okhttp.HttpResponseCache;
+import com.android.okhttp.AndroidShimResponseCache;
+
import com.google.mockwebserver.MockResponse;
import com.google.mockwebserver.MockWebServer;
import com.google.mockwebserver.RecordedRequest;
@@ -37,11 +38,14 @@ import java.net.ProtocolException;
import java.net.Proxy;
import java.net.ResponseCache;
import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
+import java.nio.channels.SocketChannel;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@@ -58,18 +62,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
-import javax.net.SocketFactory;
+import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
-import libcore.java.security.StandardNames;
import libcore.java.security.TestKeyStore;
import libcore.java.util.AbstractResourceLeakageDetectorTestCase;
import libcore.javax.net.ssl.TestSSLContext;
@@ -84,7 +88,7 @@ import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
public final class URLConnectionTest extends AbstractResourceLeakageDetectorTestCase {
private MockWebServer server;
- private HttpResponseCache cache;
+ private AndroidShimResponseCache cache;
private String hostName;
@Override protected void setUp() throws Exception {
@@ -674,11 +678,144 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
private void initResponseCache() throws IOException {
String tmp = System.getProperty("java.io.tmpdir");
File cacheDir = new File(tmp, "HttpCache-" + UUID.randomUUID());
- cache = new HttpResponseCache(cacheDir, Integer.MAX_VALUE);
+ cache = AndroidShimResponseCache.create(cacheDir, Integer.MAX_VALUE);
ResponseCache.setDefault(cache);
}
/**
+ * Test Etag headers are returned correctly when a client-side cache is not installed.
+ * https://code.google.com/p/android/issues/detail?id=108949
+ */
+ public void testEtagHeaders_uncached() throws Exception {
+ final String etagValue1 = "686897696a7c876b7e";
+ final String body1 = "Response with etag 1";
+ final String etagValue2 = "686897696a7c876b7f";
+ final String body2 = "Response with etag 2";
+
+ server.enqueue(
+ new MockResponse()
+ .setBody(body1)
+ .setHeader("Content-Type", "text/plain")
+ .setHeader("Etag", etagValue1));
+ server.enqueue(
+ new MockResponse()
+ .setBody(body2)
+ .setHeader("Content-Type", "text/plain")
+ .setHeader("Etag", etagValue2));
+ server.play();
+
+ URL url = server.getUrl("/");
+ HttpURLConnection connection1 = (HttpURLConnection) url.openConnection();
+ assertEquals(etagValue1, connection1.getHeaderField("Etag"));
+ assertContent(body1, connection1);
+ connection1.disconnect();
+
+ // Discard the server-side record of the request made.
+ server.takeRequest();
+
+ HttpURLConnection connection2 = (HttpURLConnection) url.openConnection();
+ assertEquals(etagValue2, connection2.getHeaderField("Etag"));
+ assertContent(body2, connection2);
+ connection2.disconnect();
+
+ // Check the client did not cache.
+ RecordedRequest request = server.takeRequest();
+ assertNull(request.getHeader("If-None-Match"));
+ }
+
+ /**
+ * Test Etag headers are returned correctly when a client-side cache is installed and the server
+ * data is unchanged.
+ * https://code.google.com/p/android/issues/detail?id=108949
+ */
+ public void testEtagHeaders_cachedWithServerHit() throws Exception {
+ final String etagValue = "686897696a7c876b7e";
+ final String body = "Response with etag";
+
+ server.enqueue(
+ new MockResponse()
+ .setBody(body)
+ .setResponseCode(HttpURLConnection.HTTP_OK)
+ .setHeader("Content-Type", "text/plain")
+ .setHeader("Etag", etagValue));
+
+ server.enqueue(
+ new MockResponse()
+ .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+ server.play();
+
+ initResponseCache();
+
+ URL url = server.getUrl("/");
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ assertEquals(etagValue, connection.getHeaderField("Etag"));
+ assertContent(body, connection);
+ connection.disconnect();
+
+ // Discard the server-side record of the request made.
+ server.takeRequest();
+
+ // Confirm the cached body is returned along with the original etag header.
+ HttpURLConnection cachedConnection = (HttpURLConnection) url.openConnection();
+ assertEquals(etagValue, cachedConnection.getHeaderField("Etag"));
+ assertContent(body, cachedConnection);
+ cachedConnection.disconnect();
+
+ // Check the client formatted the request correctly.
+ RecordedRequest request = server.takeRequest();
+ assertEquals(etagValue, request.getHeader("If-None-Match"));
+ }
+
+ /**
+ * Test Etag headers are returned correctly when a client-side cache is installed and the server
+ * data has changed.
+ * https://code.google.com/p/android/issues/detail?id=108949
+ */
+ public void testEtagHeaders_cachedWithServerMiss() throws Exception {
+ final String etagValue1 = "686897696a7c876b7e";
+ final String body1 = "Response with etag 1";
+ final String etagValue2 = "686897696a7c876b7f";
+ final String body2 = "Response with etag 2";
+
+ server.enqueue(
+ new MockResponse()
+ .setBody(body1)
+ .setResponseCode(HttpURLConnection.HTTP_OK)
+ .setHeader("Content-Type", "text/plain")
+ .setHeader("Etag", etagValue1));
+
+ server.enqueue(
+ new MockResponse()
+ .setBody(body2)
+ .setResponseCode(HttpURLConnection.HTTP_OK)
+ .setHeader("Content-Type", "text/plain")
+ .setHeader("Etag", etagValue2));
+
+ server.play();
+
+ initResponseCache();
+
+ URL url = server.getUrl("/");
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ assertEquals(etagValue1, connection.getHeaderField("Etag"));
+ assertContent(body1, connection);
+ connection.disconnect();
+
+ // Discard the server-side record of the request made.
+ server.takeRequest();
+
+ // Confirm the new body is returned along with the new etag header.
+ HttpURLConnection cachedConnection = (HttpURLConnection) url.openConnection();
+ assertEquals(etagValue2, cachedConnection.getHeaderField("Etag"));
+ assertContent(body2, cachedConnection);
+ cachedConnection.disconnect();
+
+ // Check the client formatted the request correctly.
+ RecordedRequest request = server.takeRequest();
+ assertEquals(etagValue1, request.getHeader("If-None-Match"));
+ }
+
+ /**
* Test which headers are sent unencrypted to the HTTP proxy.
*/
public void testProxyConnectIncludesProxyHeadersOnly()
@@ -2074,6 +2211,19 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
connection.disconnect();
}
+ public void testLastModified() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Last-Modified", "Wed, 27 Nov 2013 11:26:00 GMT")
+ .setBody("Hello"));
+ server.play();
+
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ connection.connect();
+
+ assertEquals(1385551560000L, connection.getLastModified());
+ assertEquals(1385551560000L, connection.getHeaderFieldDate("Last-Modified", -1));
+ }
+
public void testClientSendsContentLength() throws Exception {
server.enqueue(new MockResponse().setBody("A"));
server.play();
@@ -2185,52 +2335,107 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
urlConnection.getInputStream();
}
- public void testSslFallback() throws Exception {
+ public void testSslFallback_allSupportedProtocols() throws Exception {
TestSSLContext testSSLContext = TestSSLContext.create();
- // This server socket factory only supports SSLv3. This is to avoid issues due to SCSV
- // checks. See https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00
+ String[] allSupportedProtocols = { "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3" };
SSLSocketFactory serverSocketFactory =
new LimitedProtocolsSocketFactory(
testSSLContext.serverContext.getSocketFactory(),
- "SSLv3");
-
+ allSupportedProtocols);
server.useHttps(serverSocketFactory, false);
server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
- server.enqueue(new MockResponse().setBody("This required a 2nd handshake"));
+ server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+ server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+ server.enqueue(new MockResponse().setBody("This required fallbacks"));
server.play();
HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
- // Keep track of the client sockets created so that we can interrogate them.
- RecordingSocketFactory clientSocketFactory =
- new RecordingSocketFactory(testSSLContext.clientContext.getSocketFactory());
+ // Keeps track of the client sockets created so that we can interrogate them.
+ final boolean disableFallbackScsv = true;
+ FallbackTestClientSocketFactory clientSocketFactory = new FallbackTestClientSocketFactory(
+ new LimitedProtocolsSocketFactory(
+ testSSLContext.clientContext.getSocketFactory(), allSupportedProtocols),
+ disableFallbackScsv);
connection.setSSLSocketFactory(clientSocketFactory);
- assertEquals("This required a 2nd handshake",
+ assertEquals("This required fallbacks",
readAscii(connection.getInputStream(), Integer.MAX_VALUE));
+ // Confirm the server accepted a single connection.
RecordedRequest retry = server.takeRequest();
assertEquals(0, retry.getSequenceNumber());
assertEquals("SSLv3", retry.getSslProtocol());
// Confirm the client fallback looks ok.
List<SSLSocket> createdSockets = clientSocketFactory.getCreatedSockets();
- assertEquals(2, createdSockets.size());
- SSLSocket clientSocket1 = createdSockets.get(0);
- List<String> clientSocket1EnabledProtocols = Arrays.asList(
- clientSocket1.getEnabledProtocols());
- assertContains(clientSocket1EnabledProtocols, "TLSv1.2");
- List<String> clientSocket1EnabledCiphers =
- Arrays.asList(clientSocket1.getEnabledCipherSuites());
- assertContainsNoneMatching(
- clientSocket1EnabledCiphers, StandardNames.CIPHER_SUITE_FALLBACK);
-
- SSLSocket clientSocket2 = createdSockets.get(1);
- List<String> clientSocket2EnabledProtocols =
- Arrays.asList(clientSocket2.getEnabledProtocols());
- assertContainsNoneMatching(clientSocket2EnabledProtocols, "TLSv1.2");
- List<String> clientSocket2EnabledCiphers =
- Arrays.asList(clientSocket2.getEnabledCipherSuites());
- assertContains(clientSocket2EnabledCiphers, StandardNames.CIPHER_SUITE_FALLBACK);
+ assertEquals(4, createdSockets.size());
+ TlsFallbackDisabledScsvSSLSocket clientSocket1 =
+ (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(0);
+ assertSslSocket(clientSocket1,
+ false /* expectedWasFallbackScsvSet */, "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3");
+
+ TlsFallbackDisabledScsvSSLSocket clientSocket2 =
+ (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(1);
+ assertSslSocket(clientSocket2,
+ true /* expectedWasFallbackScsvSet */, "TLSv1.1", "TLSv1", "SSLv3");
+
+ TlsFallbackDisabledScsvSSLSocket clientSocket3 =
+ (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(2);
+ assertSslSocket(clientSocket3, true /* expectedWasFallbackScsvSet */, "TLSv1", "SSLv3");
+
+ TlsFallbackDisabledScsvSSLSocket clientSocket4 =
+ (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(3);
+ assertSslSocket(clientSocket4, true /* expectedWasFallbackScsvSet */, "SSLv3");
+ }
+
+ public void testSslFallback_defaultProtocols() throws Exception {
+ TestSSLContext testSSLContext = TestSSLContext.create();
+
+ server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
+ server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+ server.enqueue(new MockResponse().setSocketPolicy(FAIL_HANDSHAKE));
+ server.enqueue(new MockResponse().setBody("This required fallbacks"));
+ server.play();
+
+ HttpsURLConnection connection = (HttpsURLConnection) server.getUrl("/").openConnection();
+ // Keeps track of the client sockets created so that we can interrogate them.
+ final boolean disableFallbackScsv = true;
+ FallbackTestClientSocketFactory clientSocketFactory = new FallbackTestClientSocketFactory(
+ testSSLContext.clientContext.getSocketFactory(),
+ disableFallbackScsv);
+ connection.setSSLSocketFactory(clientSocketFactory);
+ assertEquals("This required fallbacks",
+ readAscii(connection.getInputStream(), Integer.MAX_VALUE));
+
+ // Confirm the server accepted a single connection.
+ RecordedRequest retry = server.takeRequest();
+ assertEquals(0, retry.getSequenceNumber());
+ assertEquals("TLSv1", retry.getSslProtocol());
+
+ // Confirm the client fallback looks ok.
+ List<SSLSocket> createdSockets = clientSocketFactory.getCreatedSockets();
+ assertEquals(3, createdSockets.size());
+ TlsFallbackDisabledScsvSSLSocket clientSocket1 =
+ (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(0);
+ assertSslSocket(clientSocket1,
+ false /* expectedWasFallbackScsvSet */, "TLSv1.2", "TLSv1.1", "TLSv1");
+
+ TlsFallbackDisabledScsvSSLSocket clientSocket2 =
+ (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(1);
+ assertSslSocket(clientSocket2, true /* expectedWasFallbackScsvSet */, "TLSv1.1", "TLSv1");
+
+ TlsFallbackDisabledScsvSSLSocket clientSocket3 =
+ (TlsFallbackDisabledScsvSSLSocket) createdSockets.get(2);
+ assertSslSocket(clientSocket3, true /* expectedWasFallbackScsvSet */, "TLSv1");
+ }
+
+ private static void assertSslSocket(TlsFallbackDisabledScsvSSLSocket socket,
+ boolean expectedWasFallbackScsvSet, String... expectedEnabledProtocols) {
+ Set<String> enabledProtocols =
+ new HashSet<String>(Arrays.asList(socket.getEnabledProtocols()));
+ Set<String> expectedProtocolsSet = new HashSet<String>(Arrays.asList(expectedEnabledProtocols));
+ assertEquals(expectedProtocolsSet, enabledProtocols);
+ assertEquals(expectedWasFallbackScsvSet, socket.wasTlsFallbackScsvSet());
}
public void testInspectSslBeforeConnect() throws Exception {
@@ -2491,36 +2696,37 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
}
@Override
- public Socket createSocket(Socket s, String host, int port, boolean autoClose)
+ public SSLSocket createSocket(Socket s, String host, int port, boolean autoClose)
throws IOException {
- return delegate.createSocket(s, host, port, autoClose);
+ return (SSLSocket) delegate.createSocket(s, host, port, autoClose);
}
@Override
- public Socket createSocket() throws IOException {
- return delegate.createSocket();
+ public SSLSocket createSocket() throws IOException {
+ return (SSLSocket) delegate.createSocket();
}
@Override
- public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
- return delegate.createSocket(host, port);
+ public SSLSocket createSocket(String host, int port)
+ throws IOException, UnknownHostException {
+ return (SSLSocket) delegate.createSocket(host, port);
}
@Override
- public Socket createSocket(String host, int port, InetAddress localHost,
+ public SSLSocket createSocket(String host, int port, InetAddress localHost,
int localPort) throws IOException, UnknownHostException {
- return delegate.createSocket(host, port, localHost, localPort);
+ return (SSLSocket) delegate.createSocket(host, port, localHost, localPort);
}
@Override
- public Socket createSocket(InetAddress host, int port) throws IOException {
- return delegate.createSocket(host, port);
+ public SSLSocket createSocket(InetAddress host, int port) throws IOException {
+ return (SSLSocket) delegate.createSocket(host, port);
}
@Override
- public Socket createSocket(InetAddress address, int port,
+ public SSLSocket createSocket(InetAddress address, int port,
InetAddress localAddress, int localPort) throws IOException {
- return delegate.createSocket(address, port, localAddress, localPort);
+ return (SSLSocket) delegate.createSocket(address, port, localAddress, localPort);
}
}
@@ -2539,7 +2745,7 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
}
@Override
- public Socket createSocket(Socket s, String host, int port, boolean autoClose)
+ public SSLSocket createSocket(Socket s, String host, int port, boolean autoClose)
throws IOException {
SSLSocket socket = (SSLSocket) delegate.createSocket(s, host, port, autoClose);
socket.setEnabledProtocols(protocols);
@@ -2547,21 +2753,22 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
}
@Override
- public Socket createSocket() throws IOException {
+ public SSLSocket createSocket() throws IOException {
SSLSocket socket = (SSLSocket) delegate.createSocket();
socket.setEnabledProtocols(protocols);
return socket;
}
@Override
- public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
+ public SSLSocket createSocket(String host, int port)
+ throws IOException, UnknownHostException {
SSLSocket socket = (SSLSocket) delegate.createSocket(host, port);
socket.setEnabledProtocols(protocols);
return socket;
}
@Override
- public Socket createSocket(String host, int port, InetAddress localHost,
+ public SSLSocket createSocket(String host, int port, InetAddress localHost,
int localPort) throws IOException, UnknownHostException {
SSLSocket socket = (SSLSocket) delegate.createSocket(host, port, localHost, localPort);
socket.setEnabledProtocols(protocols);
@@ -2569,14 +2776,14 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
}
@Override
- public Socket createSocket(InetAddress host, int port) throws IOException {
+ public SSLSocket createSocket(InetAddress host, int port) throws IOException {
SSLSocket socket = (SSLSocket) delegate.createSocket(host, port);
socket.setEnabledProtocols(protocols);
return socket;
}
@Override
- public Socket createSocket(InetAddress address, int port,
+ public SSLSocket createSocket(InetAddress address, int port,
InetAddress localAddress, int localPort) throws IOException {
SSLSocket socket =
(SSLSocket) delegate.createSocket(address, port, localAddress, localPort);
@@ -2586,58 +2793,337 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
}
/**
- * An SSLSocketFactory that delegates calls and keeps a record of any sockets created.
+ * An {@link javax.net.ssl.SSLSocket} that delegates all calls.
*/
- private static class RecordingSocketFactory extends DelegatingSSLSocketFactory {
+ private static abstract class DelegatingSSLSocket extends SSLSocket {
+ protected final SSLSocket delegate;
+
+ public DelegatingSSLSocket(SSLSocket delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override public void shutdownInput() throws IOException {
+ delegate.shutdownInput();
+ }
+
+ @Override public void shutdownOutput() throws IOException {
+ delegate.shutdownOutput();
+ }
+
+ @Override public String[] getSupportedCipherSuites() {
+ return delegate.getSupportedCipherSuites();
+ }
+
+ @Override public String[] getEnabledCipherSuites() {
+ return delegate.getEnabledCipherSuites();
+ }
+
+ @Override public void setEnabledCipherSuites(String[] suites) {
+ delegate.setEnabledCipherSuites(suites);
+ }
+
+ @Override public String[] getSupportedProtocols() {
+ return delegate.getSupportedProtocols();
+ }
+
+ @Override public String[] getEnabledProtocols() {
+ return delegate.getEnabledProtocols();
+ }
+
+ @Override public void setEnabledProtocols(String[] protocols) {
+ delegate.setEnabledProtocols(protocols);
+ }
+
+ @Override public SSLSession getSession() {
+ return delegate.getSession();
+ }
+
+ @Override public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
+ delegate.addHandshakeCompletedListener(listener);
+ }
+
+ @Override public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
+ delegate.removeHandshakeCompletedListener(listener);
+ }
+
+ @Override public void startHandshake() throws IOException {
+ delegate.startHandshake();
+ }
+
+ @Override public void setUseClientMode(boolean mode) {
+ delegate.setUseClientMode(mode);
+ }
+
+ @Override public boolean getUseClientMode() {
+ return delegate.getUseClientMode();
+ }
+
+ @Override public void setNeedClientAuth(boolean need) {
+ delegate.setNeedClientAuth(need);
+ }
+
+ @Override public void setWantClientAuth(boolean want) {
+ delegate.setWantClientAuth(want);
+ }
+
+ @Override public boolean getNeedClientAuth() {
+ return delegate.getNeedClientAuth();
+ }
+
+ @Override public boolean getWantClientAuth() {
+ return delegate.getWantClientAuth();
+ }
+ @Override public void setEnableSessionCreation(boolean flag) {
+ delegate.setEnableSessionCreation(flag);
+ }
+
+ @Override public boolean getEnableSessionCreation() {
+ return delegate.getEnableSessionCreation();
+ }
+
+ @Override public SSLParameters getSSLParameters() {
+ return delegate.getSSLParameters();
+ }
+
+ @Override public void setSSLParameters(SSLParameters p) {
+ delegate.setSSLParameters(p);
+ }
+
+ @Override public void close() throws IOException {
+ delegate.close();
+ }
+
+ @Override public InetAddress getInetAddress() {
+ return delegate.getInetAddress();
+ }
+
+ @Override public InputStream getInputStream() throws IOException {
+ return delegate.getInputStream();
+ }
+
+ @Override public boolean getKeepAlive() throws SocketException {
+ return delegate.getKeepAlive();
+ }
+
+ @Override public InetAddress getLocalAddress() {
+ return delegate.getLocalAddress();
+ }
+
+ @Override public int getLocalPort() {
+ return delegate.getLocalPort();
+ }
+
+ @Override public OutputStream getOutputStream() throws IOException {
+ return delegate.getOutputStream();
+ }
+
+ @Override public int getPort() {
+ return delegate.getPort();
+ }
+
+ @Override public int getSoLinger() throws SocketException {
+ return delegate.getSoLinger();
+ }
+
+ @Override public int getReceiveBufferSize() throws SocketException {
+ return delegate.getReceiveBufferSize();
+ }
+
+ @Override public int getSendBufferSize() throws SocketException {
+ return delegate.getSendBufferSize();
+ }
+
+ @Override public int getSoTimeout() throws SocketException {
+ return delegate.getSoTimeout();
+ }
+
+ @Override public boolean getTcpNoDelay() throws SocketException {
+ return delegate.getTcpNoDelay();
+ }
+
+ @Override public void setKeepAlive(boolean keepAlive) throws SocketException {
+ delegate.setKeepAlive(keepAlive);
+ }
+
+ @Override public void setSendBufferSize(int size) throws SocketException {
+ delegate.setSendBufferSize(size);
+ }
+
+ @Override public void setReceiveBufferSize(int size) throws SocketException {
+ delegate.setReceiveBufferSize(size);
+ }
+
+ @Override public void setSoLinger(boolean on, int timeout) throws SocketException {
+ delegate.setSoLinger(on, timeout);
+ }
+
+ @Override public void setSoTimeout(int timeout) throws SocketException {
+ delegate.setSoTimeout(timeout);
+ }
+
+ @Override public void setTcpNoDelay(boolean on) throws SocketException {
+ delegate.setTcpNoDelay(on);
+ }
+
+ @Override public String toString() {
+ return delegate.toString();
+ }
+
+ @Override public SocketAddress getLocalSocketAddress() {
+ return delegate.getLocalSocketAddress();
+ }
+
+ @Override public SocketAddress getRemoteSocketAddress() {
+ return delegate.getRemoteSocketAddress();
+ }
+
+ @Override public boolean isBound() {
+ return delegate.isBound();
+ }
+
+ @Override public boolean isConnected() {
+ return delegate.isConnected();
+ }
+
+ @Override public boolean isClosed() {
+ return delegate.isClosed();
+ }
+
+ @Override public void bind(SocketAddress localAddr) throws IOException {
+ delegate.bind(localAddr);
+ }
+
+ @Override public void connect(SocketAddress remoteAddr) throws IOException {
+ delegate.connect(remoteAddr);
+ }
+
+ @Override public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
+ delegate.connect(remoteAddr, timeout);
+ }
+
+ @Override public boolean isInputShutdown() {
+ return delegate.isInputShutdown();
+ }
+
+ @Override public boolean isOutputShutdown() {
+ return delegate.isOutputShutdown();
+ }
+
+ @Override public void setReuseAddress(boolean reuse) throws SocketException {
+ delegate.setReuseAddress(reuse);
+ }
+
+ @Override public boolean getReuseAddress() throws SocketException {
+ return delegate.getReuseAddress();
+ }
+
+ @Override public void setOOBInline(boolean oobinline) throws SocketException {
+ delegate.setOOBInline(oobinline);
+ }
+
+ @Override public boolean getOOBInline() throws SocketException {
+ return delegate.getOOBInline();
+ }
+
+ @Override public void setTrafficClass(int value) throws SocketException {
+ delegate.setTrafficClass(value);
+ }
+
+ @Override public int getTrafficClass() throws SocketException {
+ return delegate.getTrafficClass();
+ }
+
+ @Override public void sendUrgentData(int value) throws IOException {
+ delegate.sendUrgentData(value);
+ }
+
+ @Override public SocketChannel getChannel() {
+ return delegate.getChannel();
+ }
+
+ @Override public void setPerformancePreferences(int connectionTime, int latency,
+ int bandwidth) {
+ delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
+ }
+ }
+
+ /**
+ * An SSLSocketFactory that delegates calls. It keeps a record of any sockets created.
+ * If {@link #disableTlsFallbackScsv} is set to {@code true} then sockets created by the
+ * delegate are wrapped with ones that will not accept the {@link #TLS_FALLBACK_SCSV} cipher,
+ * thus bypassing server-side fallback checks on platforms that support it. Unfortunately this
+ * wrapping will disable any reflection-based calls to SSLSocket from Platform.
+ */
+ private static class FallbackTestClientSocketFactory extends DelegatingSSLSocketFactory {
+ /**
+ * The cipher suite used during TLS connection fallback to indicate a fallback.
+ * See https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00
+ */
+ public static final String TLS_FALLBACK_SCSV = "TLS_FALLBACK_SCSV";
+
+ private final boolean disableTlsFallbackScsv;
private final List<SSLSocket> createdSockets = new ArrayList<SSLSocket>();
- private RecordingSocketFactory(SSLSocketFactory delegate) {
+ public FallbackTestClientSocketFactory(SSLSocketFactory delegate,
+ boolean disableTlsFallbackScsv) {
super(delegate);
+ this.disableTlsFallbackScsv = disableTlsFallbackScsv;
}
- @Override
- public Socket createSocket(Socket s, String host, int port, boolean autoClose)
+ @Override public SSLSocket createSocket(Socket s, String host, int port, boolean autoClose)
throws IOException {
- SSLSocket socket = (SSLSocket) delegate.createSocket(s, host, port, autoClose);
+ SSLSocket socket = super.createSocket(s, host, port, autoClose);
+ if (disableTlsFallbackScsv) {
+ socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+ }
createdSockets.add(socket);
return socket;
}
- @Override
- public Socket createSocket() throws IOException {
- SSLSocket socket = (SSLSocket) delegate.createSocket();
+ @Override public SSLSocket createSocket() throws IOException {
+ SSLSocket socket = super.createSocket();
+ if (disableTlsFallbackScsv) {
+ socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+ }
createdSockets.add(socket);
return socket;
}
- @Override
- public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
- SSLSocket socket = (SSLSocket) delegate.createSocket(host, port);
+ @Override public SSLSocket createSocket(String host,int port) throws IOException {
+ SSLSocket socket = super.createSocket(host, port);
+ if (disableTlsFallbackScsv) {
+ socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+ }
createdSockets.add(socket);
return socket;
}
- @Override
- public Socket createSocket(String host, int port, InetAddress localHost,
- int localPort) throws IOException, UnknownHostException {
- SSLSocket socket = (SSLSocket) delegate.createSocket(host, port, localHost, localPort);
+ @Override public SSLSocket createSocket(String host,int port, InetAddress localHost,
+ int localPort) throws IOException {
+ SSLSocket socket = super.createSocket(host, port, localHost, localPort);
+ if (disableTlsFallbackScsv) {
+ socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+ }
createdSockets.add(socket);
return socket;
}
- @Override
- public Socket createSocket(InetAddress host, int port) throws IOException {
- SSLSocket socket = (SSLSocket) delegate.createSocket(host, port);
+ @Override public SSLSocket createSocket(InetAddress host,int port) throws IOException {
+ SSLSocket socket = super.createSocket(host, port);
+ if (disableTlsFallbackScsv) {
+ socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+ }
createdSockets.add(socket);
return socket;
}
- @Override
- public Socket createSocket(InetAddress address, int port,
+ @Override public SSLSocket createSocket(InetAddress address,int port,
InetAddress localAddress, int localPort) throws IOException {
- SSLSocket socket =
- (SSLSocket) delegate.createSocket(address, port, localAddress, localPort);
+ SSLSocket socket = super.createSocket(address, port, localAddress, localPort);
+ if (disableTlsFallbackScsv) {
+ socket = new TlsFallbackDisabledScsvSSLSocket(socket);
+ }
createdSockets.add(socket);
return socket;
}
@@ -2647,4 +3133,31 @@ public final class URLConnectionTest extends AbstractResourceLeakageDetectorTest
}
}
+ private static class TlsFallbackDisabledScsvSSLSocket extends DelegatingSSLSocket {
+
+ private boolean tlsFallbackScsvSet;
+
+ public TlsFallbackDisabledScsvSSLSocket(SSLSocket socket) {
+ super(socket);
+ }
+
+ @Override public void setEnabledCipherSuites(String[] suites) {
+ List<String> enabledCipherSuites = new ArrayList<String>(suites.length);
+ for (String suite : suites) {
+ if (suite.equals(FallbackTestClientSocketFactory.TLS_FALLBACK_SCSV)) {
+ // Record that an attempt was made to set TLS_FALLBACK_SCSV, but don't actually
+ // set it.
+ tlsFallbackScsvSet = true;
+ } else {
+ enabledCipherSuites.add(suite);
+ }
+ }
+ delegate.setEnabledCipherSuites(
+ enabledCipherSuites.toArray(new String[enabledCipherSuites.size()]));
+ }
+
+ public boolean wasTlsFallbackScsvSet() {
+ return tlsFallbackScsvSet;
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java b/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
index 9789197..41b434d 100644
--- a/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
@@ -15,7 +15,6 @@
*/
package libcore.java.nio.channels;
-import android.system.OsConstants;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
@@ -28,7 +27,6 @@ import java.nio.channels.SocketChannel;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import junit.framework.TestCase;
-import libcore.io.Libcore;
import tests.net.StuckServer;
public class SelectorTest extends TestCase {
@@ -71,25 +69,6 @@ public class SelectorTest extends TestCase {
}
}
- // http://b/6453247
- // This test won't work on the host until/unless we start using libcorkscrew there.
- // The runtime itself blocks SIGQUIT, so that doesn't cause poll(2) to EINTR directly.
- // The EINTR is caused by the way libcorkscrew works.
- public void testEINTR() throws Exception {
- Selector selector = Selector.open();
- new Thread(new Runnable() {
- @Override public void run() {
- try {
- Thread.sleep(2000);
- Libcore.os.kill(Libcore.os.getpid(), OsConstants.SIGQUIT);
- } catch (Exception ex) {
- fail();
- }
- }
- }).start();
- assertEquals(0, selector.select());
- }
-
// http://code.google.com/p/android/issues/detail?id=15388
public void testInterrupted() throws IOException {
Selector selector = Selector.open();
diff --git a/luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java b/luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java
index ff510e0..e9ab8ae 100644
--- a/luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java
+++ b/luni/src/test/java/libcore/java/nio/charset/CharsetEncoderTest.java
@@ -20,8 +20,10 @@ import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
public class CharsetEncoderTest extends junit.framework.TestCase {
@@ -161,4 +163,71 @@ public class CharsetEncoderTest extends junit.framework.TestCase {
assertEquals(CoderResult.UNDERFLOW, cr);
assertEquals(8, bb.position());
}
+
+ // Discards all input. Outputs a single byte 'X' on flush.
+ private static final class MockCharset extends Charset {
+ static final Charset INSTANCE = new MockCharset();
+
+ private MockCharset() {
+ super("MockCharset", new String[0]);
+ }
+
+ public boolean contains(Charset charset) {
+ return false;
+ }
+
+ public CharsetEncoder newEncoder() {
+ return new CharsetEncoder(INSTANCE, 1.f, 1.f) {
+ protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
+ in.position(in.limit());
+ return CoderResult.UNDERFLOW;
+ }
+
+ protected CoderResult implFlush(ByteBuffer out) {
+ out.put((byte) 'X');
+ return CoderResult.UNDERFLOW;
+ }
+ };
+ }
+
+ public CharsetDecoder newDecoder() {
+ return new CharsetDecoder(INSTANCE, 1.f, 1.f) {
+ protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
+ in.position(in.limit());
+ return CoderResult.UNDERFLOW;
+ }
+ };
+ }
+ }
+
+ // Repeated calls to flush() should not result in repeated calls to implFlush().
+ public void testFlushNotCallingImplFlushRepeatedly() {
+ CharsetEncoder e = MockCharset.INSTANCE.newEncoder();
+ ByteBuffer bb = ByteBuffer.allocate(4);
+ CoderResult cr = e.encode(CharBuffer.allocate(0), bb, true);
+ assertEquals(CoderResult.UNDERFLOW, cr);
+ cr = e.flush(bb);
+ assertEquals(CoderResult.UNDERFLOW, cr);
+ cr = e.flush(bb);
+ assertEquals(CoderResult.UNDERFLOW, cr);
+ assertEquals(1, bb.position());
+ assertEquals((byte) 'X', bb.get(0));
+ assertEquals(0x00, bb.get(1));
+ assertEquals(0x00, bb.get(2));
+ assertEquals(0x00, bb.get(3));
+ }
+
+ // http://b/19185235
+ public void testFlushWithIncompleteInput() {
+ CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
+ ByteBuffer output = ByteBuffer.allocate(10);
+ CoderResult result = encoder.encode(CharBuffer.wrap("\ud800"), output,
+ true /* endOfInput */);
+ assertTrue(result.isUnderflow());
+
+ result = encoder.flush(output);
+ assertTrue(result.isMalformed());
+ assertEquals(1, result.length());
+ assertEquals(0, output.position());
+ }
}
diff --git a/luni/src/test/java/libcore/java/nio/charset/SettableCharsetProvider.java b/luni/src/test/java/libcore/java/nio/charset/SettableCharsetProvider.java
new file mode 100644
index 0000000..b4886d2
--- /dev/null
+++ b/luni/src/test/java/libcore/java/nio/charset/SettableCharsetProvider.java
@@ -0,0 +1,57 @@
+/*
+ * 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 libcore.java.nio.charset;
+
+import java.nio.charset.Charset;
+import java.nio.charset.spi.CharsetProvider;
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * This class is registered as a charset provider by the META-INF in the libcore
+ * tests jar. Since there isn't any convenient API to dynamically register and de-register
+ * charset-providers, this class allows tests to plug in a delegate that lives for the
+ * duration of the test.
+ */
+public final class SettableCharsetProvider extends CharsetProvider {
+ private static CharsetProvider delegate;
+
+ public static void setDelegate(CharsetProvider cp) {
+ delegate = cp;
+ }
+
+ public static void clearDelegate() {
+ delegate = null;
+ }
+
+ @Override
+ public Iterator<Charset> charsets() {
+ if (delegate != null) {
+ return delegate.charsets();
+ }
+
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public Charset charsetForName(String charsetName) {
+ if (delegate != null) {
+ return delegate.charsetForName(charsetName);
+ }
+
+ return null;
+ }
+}
diff --git a/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java b/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java
index e7fdb1f..7e08b5f 100644
--- a/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java
+++ b/luni/src/test/java/libcore/java/security/KeyPairGeneratorTest.java
@@ -121,7 +121,10 @@ public class KeyPairGeneratorTest extends TestCase {
}
String algorithm = service.getAlgorithm();
- // AndroidKeyStore is tested in CTS.
+ // Do not test AndroidKeyStore's KeyPairGenerator. It cannot be initialized without
+ // providing AndroidKeyStore-specific algorithm parameters.
+ // It's OKish not to test AndroidKeyStore's KeyPairGenerator here because it's tested
+ // by cts/tests/test/keystore.
if ("AndroidKeyStore".equals(provider.getName())) {
continue;
}
diff --git a/luni/src/test/java/libcore/java/security/ProviderTest.java b/luni/src/test/java/libcore/java/security/ProviderTest.java
index 994214b..0be558e 100644
--- a/luni/src/test/java/libcore/java/security/ProviderTest.java
+++ b/luni/src/test/java/libcore/java/security/ProviderTest.java
@@ -160,7 +160,7 @@ public class ProviderTest extends TestCase {
public void test_Provider_Properties() throws Exception {
/*
* A useful reference on Provider properties
- * <a href="http://java.sun.com/javase/6/docs/technotes/guides/security/crypto/HowToImplAProvider.html>
+ * <a href="http://java.sun.com/javase/6/docs/technotes/guides/security/crypto/HowToImplAProvider.html">
* How to Implement a Provider in the Java &trade; Cryptography Architecture
* </a>
*/
diff --git a/luni/src/test/java/libcore/java/security/SignatureTest.java b/luni/src/test/java/libcore/java/security/SignatureTest.java
index 5e02f10..e546f4f 100644
--- a/luni/src/test/java/libcore/java/security/SignatureTest.java
+++ b/luni/src/test/java/libcore/java/security/SignatureTest.java
@@ -71,6 +71,24 @@ public class SignatureTest extends TestCase {
}
}
+ public void testSignature_getInstance_DoesNotSupportKeyClass_Success() throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Signature.FOO", MockSignatureSpi.AllKeyTypes.class.getName());
+ put("Signature.FOO SupportedKeyClasses", "None");
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ Signature s = Signature.getInstance("FOO", mockProvider);
+ s.initSign(new MockPrivateKey());
+ assertEquals(mockProvider, s.getProvider());
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+
public void testSignature_getInstance_OnlyUsesSpecifiedProvider_SameNameAndClass_Success()
throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
diff --git a/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java b/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java
index e2f21e8..a3a721a 100644
--- a/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java
+++ b/luni/src/test/java/libcore/java/security/cert/CertificateFactoryTest.java
@@ -16,11 +16,13 @@
package libcore.java.security.cert;
+import com.android.org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import com.android.org.bouncycastle.asn1.x509.BasicConstraints;
-import com.android.org.bouncycastle.asn1.x509.X509Extensions;
+import com.android.org.bouncycastle.asn1.x509.Extension;
+import com.android.org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
+import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
import com.android.org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
-import com.android.org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -28,13 +30,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
-import java.io.OptionalDataException;
-import java.io.StreamCorruptedException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
+import java.security.PublicKey;
import java.security.Security;
import java.security.cert.CertPath;
import java.security.cert.Certificate;
@@ -554,25 +557,26 @@ public class CertificateFactoryTest extends TestCase {
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
+ PublicKey pubKey = keyPair.getPublic();
certGen.setSerialNumber(serial);
certGen.setIssuerDN(issuerPrincipal);
certGen.setNotBefore(startDate);
certGen.setNotAfter(expiryDate);
certGen.setSubjectDN(subjectPrincipal);
- certGen.setPublicKey(keyPair.getPublic());
+ certGen.setPublicKey(pubKey);
certGen.setSignatureAlgorithm("SHA1withRSA");
if (issuer != null) {
- certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
+ certGen.addExtension(Extension.authorityKeyIdentifier, false,
new AuthorityKeyIdentifierStructure(issuer.certificate));
} else {
- certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
- new AuthorityKeyIdentifierStructure(keyPair.getPublic()));
+ certGen.addExtension(Extension.authorityKeyIdentifier, false,
+ new AuthorityKeyIdentifier(generatePublicKeyDigest(pubKey)));
}
- certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false,
- new SubjectKeyIdentifierStructure(keyPair.getPublic()));
- certGen.addExtension(X509Extensions.BasicConstraints, true, basicConstraints);
+ certGen.addExtension(Extension.subjectKeyIdentifier, false,
+ new SubjectKeyIdentifier(generatePublicKeyDigest(pubKey)));
+ certGen.addExtension(Extension.basicConstraints, true, basicConstraints);
X509Certificate cert = certGen.generate(caKey);
@@ -582,4 +586,18 @@ public class CertificateFactoryTest extends TestCase {
return holder;
}
+
+ /**
+ * Generates a type 1 key identifier according to RFC 3280 4.2.1.2.
+ */
+ private static byte[] generatePublicKeyDigest(PublicKey pubKey) {
+ SubjectPublicKeyInfo spki = SubjectPublicKeyInfo.getInstance(pubKey.getEncoded());
+ MessageDigest sha1digest;
+ try {
+ sha1digest = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("SHA-1 not available");
+ }
+ return sha1digest.digest(spki.getPublicKeyData().getBytes());
+ }
}
diff --git a/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java b/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java
index 42de50a..1611120 100644
--- a/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java
+++ b/luni/src/test/java/libcore/java/security/cert/X509CRLTest.java
@@ -256,15 +256,7 @@ public class X509CRLTest extends TestCase {
private void getSigAlgName(CertificateFactory f) throws Exception {
X509CRL crlRsa = getCRL(f, CRL_RSA);
-
- String actual = crlRsa.getSigAlgName().toUpperCase(Locale.US);
-
- // Bouncycastle is broken
- if ("BC".equals(f.getProvider().getName())) {
- assertEquals("1.2.840.113549.1.1.5", actual);
- } else {
- assertEquals("SHA1WITHRSA", actual);
- }
+ assertEquals("SHA1WITHRSA", getCRL(f, CRL_RSA).getSigAlgName().toUpperCase(Locale.ROOT));
}
private void getSigAlgOID(CertificateFactory f) throws Exception {
diff --git a/luni/src/test/java/libcore/java/sql/TimestampTest.java b/luni/src/test/java/libcore/java/sql/TimestampTest.java
index 2985848..71ac8c8 100644
--- a/luni/src/test/java/libcore/java/sql/TimestampTest.java
+++ b/luni/src/test/java/libcore/java/sql/TimestampTest.java
@@ -144,4 +144,12 @@ public final class TimestampTest extends TestCase {
} catch (IllegalArgumentException expected) { }
}
+ // http://b/19756610
+ public void testAsymmetricEquals() {
+ Timestamp timestamp = new Timestamp(0);
+ java.util.Date date = new java.util.Date(0);
+
+ assertTrue(date.equals(timestamp));
+ assertFalse(timestamp.equals(date));
+ }
}
diff --git a/luni/src/test/java/libcore/java/text/BreakIteratorTest.java b/luni/src/test/java/libcore/java/text/BreakIteratorTest.java
index 47701c8..de2ae52 100644
--- a/luni/src/test/java/libcore/java/text/BreakIteratorTest.java
+++ b/luni/src/test/java/libcore/java/text/BreakIteratorTest.java
@@ -172,32 +172,4 @@ public class BreakIteratorTest extends junit.framework.TestCase {
// Expected exception
}
}
-
- // http://code.google.com/p/android/issues/detail?id=41143
- // This code is inherently unsafe and crazy;
- // we're just trying to provoke native crashes!
- public void testConcurrentBreakIteratorAccess() throws Exception {
- final BreakIterator it = BreakIterator.getCharacterInstance();
-
- ArrayList<Thread> threads = new ArrayList<Thread>();
- for (int i = 0; i < 10; ++i) {
- Thread t = new Thread(new Runnable() {
- public void run() {
- for (int i = 0; i < 4096; ++i) {
- it.setText("some example text");
- for (int index = it.first(); index != BreakIterator.DONE; index = it.next()) {
- }
- }
- }
- });
- threads.add(t);
- }
-
- for (Thread t : threads) {
- t.start();
- }
- for (Thread t : threads) {
- t.join();
- }
- }
}
diff --git a/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java b/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
index e6933e6..0c97f34 100644
--- a/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
+++ b/luni/src/test/java/libcore/java/text/DateFormatSymbolsTest.java
@@ -147,17 +147,17 @@ public class DateFormatSymbolsTest extends junit.framework.TestCase {
}
// http://b/7955614
- public void test_getZoneStrings_GMT_short_names() throws Exception {
+ public void test_getZoneStrings_Apia() throws Exception {
String[][] array = DateFormatSymbols.getInstance(Locale.US).getZoneStrings();
for (int i = 0; i < array.length; ++i) {
String[] row = array[i];
- // America/Santiago is somewhat arbitrary; we just want a zone we have to generate
+ // Pacific/Apia is somewhat arbitrary; we just want a zone we have to generate
// "GMT" strings for the short names.
- if (row[0].equals("America/Santiago")) {
- assertEquals("Chile Standard Time", row[1]);
- assertEquals("GMT-03:00", row[2]);
- assertEquals("Chile Summer Time", row[3]);
- assertEquals("GMT-03:00", row[4]);
+ if (row[0].equals("Pacific/Apia")) {
+ assertEquals("Apia Standard Time", row[1]);
+ assertEquals("GMT+13:00", row[2]);
+ assertEquals("Apia Daylight Time", row[3]);
+ assertEquals("GMT+14:00", row[4]);
}
}
}
diff --git a/luni/src/test/java/libcore/java/text/NumberFormatTest.java b/luni/src/test/java/libcore/java/text/NumberFormatTest.java
index 0678e96..87fe96d 100644
--- a/luni/src/test/java/libcore/java/text/NumberFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/NumberFormatTest.java
@@ -17,6 +17,7 @@
package libcore.java.text;
import java.math.BigInteger;
+import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.FieldPosition;
@@ -93,6 +94,21 @@ public class NumberFormatTest extends junit.framework.TestCase {
}
}
+ public void testPercentageRounding() throws Exception {
+ NumberFormat nf = NumberFormat.getPercentInstance(Locale.US);
+ assertEquals("15%", nf.format(0.149));
+ assertEquals("14%", nf.format(0.142));
+
+ nf.setRoundingMode(RoundingMode.UP);
+ assertEquals("15%", nf.format(0.142));
+
+ nf.setRoundingMode(RoundingMode.DOWN);
+ assertEquals("14%", nf.format(0.149));
+
+ nf.setMaximumFractionDigits(1);
+ assertEquals("14.9%", nf.format(0.149));
+ }
+
// https://code.google.com/p/android/issues/detail?id=62269
public void test_62269() throws Exception {
NumberFormat nf = NumberFormat.getNumberInstance(Locale.US);
diff --git a/luni/src/test/java/libcore/java/util/CalendarTest.java b/luni/src/test/java/libcore/java/util/CalendarTest.java
index e0e1a35..2e13ad8 100644
--- a/luni/src/test/java/libcore/java/util/CalendarTest.java
+++ b/luni/src/test/java/libcore/java/util/CalendarTest.java
@@ -263,4 +263,76 @@ public class CalendarTest extends junit.framework.TestCase {
b.setTime(d);
assertEquals(a, b);
}
+
+ public void testCloneMakesDeepCopyOfCalendarFields() {
+ FakeCalendar c = new FakeCalendar();
+ FakeCalendar c2 = (FakeCalendar) c.clone();
+
+ assertFalse(c.getTimeZone() == c2.getTimeZone());
+ assertEquals(c.getTimeZone(), c2.getTimeZone());
+
+ // The default clone() implementation makes a deep copy of calendar
+ // fields...
+ assertFalse(c.getCalenderFields() == c2.getCalenderFields());
+ // ,,, and a shallow copy of subclass fields.
+ assertSame(c.getSubclassFields(), c2.getSubclassFields());
+ }
+
+ public static class FakeCalendar extends Calendar {
+
+ private int[] subclassFields;
+
+ public FakeCalendar() {
+ super(TimeZone.getDefault(), Locale.getDefault());
+ subclassFields = new int[12];
+ }
+
+ public int[] getCalenderFields() {
+ return fields;
+ }
+
+ public int[] getSubclassFields() {
+ return subclassFields;
+ }
+
+ @Override
+ public void add(int field, int value) {
+
+ }
+
+ @Override
+ protected void computeFields() {
+
+ }
+
+ @Override
+ protected void computeTime() {
+
+ }
+
+ @Override
+ public int getGreatestMinimum(int field) {
+ return 0;
+ }
+
+ @Override
+ public int getLeastMaximum(int field) {
+ return 0;
+ }
+
+ @Override
+ public int getMaximum(int field) {
+ return 0;
+ }
+
+ @Override
+ public int getMinimum(int field) {
+ return 0;
+ }
+
+ @Override
+ public void roll(int field, boolean increment) {
+
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/CollectionsTest.java b/luni/src/test/java/libcore/java/util/CollectionsTest.java
index 80c769e..bc73817 100644
--- a/luni/src/test/java/libcore/java/util/CollectionsTest.java
+++ b/luni/src/test/java/libcore/java/util/CollectionsTest.java
@@ -17,7 +17,10 @@
package libcore.java.util;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
+import java.util.ConcurrentModificationException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.ListIterator;
@@ -94,4 +97,92 @@ public final class CollectionsTest extends TestCase {
} catch (IllegalStateException expected) {
}
}
+
+ public void testSortFastPath_incrementsModcount() {
+ ArrayList<String> list = new ArrayList<String>(16);
+ list.add("coven");
+ list.add("asylum");
+ list.add("murder house");
+ list.add("freak show");
+
+ Iterator<String> it = list.iterator();
+ it.next();
+ Collections.sort(list);
+ try {
+ it.next();
+ fail();
+ } catch (ConcurrentModificationException expected) {
+ }
+ }
+
+ /**
+ * A value type whose {@code compareTo} method returns one of {@code 0},
+ * {@code Integer.MIN_VALUE} and {@code Integer.MAX_VALUE}.
+ */
+ static final class IntegerWithExtremeComparator
+ implements Comparable<IntegerWithExtremeComparator> {
+ private final int value;
+
+ public IntegerWithExtremeComparator(int value) {
+ this.value = value;
+ }
+
+ @Override
+ public int compareTo(IntegerWithExtremeComparator another) {
+ if (another.value == this.value) {
+ return 0;
+ } else if (another.value > this.value) {
+ return Integer.MIN_VALUE;
+ } else {
+ return Integer.MAX_VALUE;
+ }
+ }
+ }
+
+ // http://b/19749094
+ public void testBinarySearch_comparatorThatReturnsMinAndMaxValue() {
+ ArrayList<Integer> list = new ArrayList<Integer>(16);
+ list.add(4);
+ list.add(9);
+ list.add(11);
+ list.add(14);
+ list.add(16);
+
+ int index = Collections.binarySearch(list, 9, new Comparator<Integer>() {
+ @Override
+ public int compare(Integer lhs, Integer rhs) {
+ final int compare = lhs.compareTo(rhs);
+ if (compare == 0) {
+ return 0;
+ } else if (compare < 0) {
+ return Integer.MIN_VALUE;
+ } else {
+ return Integer.MAX_VALUE;
+ }
+ }
+ });
+ assertEquals(1, index);
+
+ ArrayList<IntegerWithExtremeComparator> list2 =
+ new ArrayList<IntegerWithExtremeComparator>();
+ list2.add(new IntegerWithExtremeComparator(4));
+ list2.add(new IntegerWithExtremeComparator(9));
+ list2.add(new IntegerWithExtremeComparator(11));
+ list2.add(new IntegerWithExtremeComparator(14));
+ list2.add(new IntegerWithExtremeComparator(16));
+
+ assertEquals(1, Collections.binarySearch(list2, new IntegerWithExtremeComparator(9)));
+ }
+
+ public void testBinarySearch_emptyCollection() {
+ assertEquals(-1, Collections.binarySearch(new ArrayList<Integer>(), 9));
+
+ assertEquals(-1, Collections.binarySearch(new ArrayList<Integer>(), 9,
+ new Comparator<Integer>() {
+ @Override
+ public int compare(Integer lhs, Integer rhs) {
+ return lhs.compareTo(rhs);
+ }
+ }));
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/CurrencyTest.java b/luni/src/test/java/libcore/java/util/CurrencyTest.java
index cf2a1b6..e4c56e5 100644
--- a/luni/src/test/java/libcore/java/util/CurrencyTest.java
+++ b/luni/src/test/java/libcore/java/util/CurrencyTest.java
@@ -58,7 +58,7 @@ public class CurrencyTest extends junit.framework.TestCase {
assertEquals("Swiss Franc", Currency.getInstance("CHF").getDisplayName(Locale.US));
assertEquals("Schweizer Franken", Currency.getInstance("CHF").getDisplayName(new Locale("de", "CH")));
assertEquals("franc suisse", Currency.getInstance("CHF").getDisplayName(new Locale("fr", "CH")));
- assertEquals("Franco Svizzero", Currency.getInstance("CHF").getDisplayName(new Locale("it", "CH")));
+ assertEquals("franco svizzero", Currency.getInstance("CHF").getDisplayName(new Locale("it", "CH")));
}
public void test_getDefaultFractionDigits() throws Exception {
diff --git a/luni/src/test/java/libcore/java/util/LocaleTest.java b/luni/src/test/java/libcore/java/util/LocaleTest.java
index c72ecd7..e1e84ab 100644
--- a/luni/src/test/java/libcore/java/util/LocaleTest.java
+++ b/luni/src/test/java/libcore/java/util/LocaleTest.java
@@ -20,6 +20,7 @@ import java.text.BreakIterator;
import java.text.Collator;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
+import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Calendar;
import java.util.IllformedLocaleException;
@@ -1146,4 +1147,60 @@ public class LocaleTest extends junit.framework.TestCase {
assertEquals("variant", locale.getVariant());
assertEquals(locale, Locale.forLanguageTag(locale.toLanguageTag()));
}
+
+ public void testArabicDigits() throws Exception {
+ // ar-DZ uses latn digits by default, but we can override that.
+ Locale ar_DZ = Locale.forLanguageTag("ar-DZ");
+ Locale ar_DZ_arab = Locale.forLanguageTag("ar-DZ-u-nu-arab");
+ Locale ar_DZ_latn = Locale.forLanguageTag("ar-DZ-u-nu-latn");
+ assertEquals('0', new DecimalFormatSymbols(ar_DZ).getZeroDigit());
+ assertEquals('\u0660', new DecimalFormatSymbols(ar_DZ_arab).getZeroDigit());
+ assertEquals('0', new DecimalFormatSymbols(ar_DZ_latn).getZeroDigit());
+
+ // ar-EG uses arab digits by default, but we can override that.
+ Locale ar_EG = Locale.forLanguageTag("ar-EG");
+ Locale ar_EG_arab = Locale.forLanguageTag("ar-EG-u-nu-arab");
+ Locale ar_EG_latn = Locale.forLanguageTag("ar-EG-u-nu-latn");
+ assertEquals('\u0660', new DecimalFormatSymbols(ar_EG).getZeroDigit());
+ assertEquals('\u0660', new DecimalFormatSymbols(ar_EG_arab).getZeroDigit());
+ assertEquals('0', new DecimalFormatSymbols(ar_EG_latn).getZeroDigit());
+ }
+
+ public void testDefaultLocale() throws Exception {
+ final String userLanguage = System.getProperty("user.language", "");
+ final String userRegion = System.getProperty("user.region", "");
+ final String userLocale = System.getProperty("user.locale", "");
+ try {
+ // Assert that user.locale gets priority.
+ System.setUnchangeableSystemProperty("user.locale", "de-DE");
+ System.setUnchangeableSystemProperty("user.language", "en");
+ System.setUnchangeableSystemProperty("user.region", "US");
+
+ Locale l = Locale.getDefaultLocaleFromSystemProperties();
+ assertEquals("de", l.getLanguage());
+ assertEquals("DE", l.getCountry());
+
+ // Assert that it's parsed as a full language tag.
+ System.setUnchangeableSystemProperty("user.locale", "de-Latn-DE");
+ System.setUnchangeableSystemProperty("user.language", "en");
+ System.setUnchangeableSystemProperty("user.region", "US");
+
+ l = Locale.getDefaultLocaleFromSystemProperties();
+ assertEquals("de", l.getLanguage());
+ assertEquals("DE", l.getCountry());
+ assertEquals("Latn", l.getScript());
+
+ // Assert that we use "und" if we're faced with a bad language tag, and
+ // that we don't end up with a null default locale or an exception.
+ System.setUnchangeableSystemProperty("user.locale", "dexx-Latn-DE");
+
+ l = Locale.getDefaultLocaleFromSystemProperties();
+ assertEquals("und", l.getLanguage());
+ assertEquals("DE", l.getCountry());
+ } finally {
+ System.setUnchangeableSystemProperty("user.language", userLanguage);
+ System.setUnchangeableSystemProperty("user.region", userRegion);
+ System.setUnchangeableSystemProperty("user.locale", userLocale);
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java b/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java
index ecf2e5f..7a5fc4a 100644
--- a/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/OldTimeZoneTest.java
@@ -108,7 +108,7 @@ public class OldTimeZoneTest extends TestCase {
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
assertEquals("Pacific Daylight Time", tz.getDisplayName(true, TimeZone.LONG, Locale.US));
assertEquals("Pacific Standard Time", tz.getDisplayName(false, TimeZone.LONG, Locale.UK));
- assertEquals("heure avanc\u00e9e du Pacifique",
+ assertEquals("heure d’été du Pacifique",
tz.getDisplayName(true, TimeZone.LONG, Locale.FRANCE));
assertEquals("heure normale du Pacifique nord-américain",
tz.getDisplayName(false, TimeZone.LONG, Locale.FRANCE));
@@ -123,18 +123,6 @@ public class OldTimeZoneTest extends TestCase {
assertEquals("GMT-07:00", tz.getDisplayName(true, TimeZone.SHORT, Locale.FRANCE));
assertEquals("GMT-08:00", tz.getDisplayName(false, TimeZone.SHORT, Locale.UK));
assertEquals("GMT-07:00", tz.getDisplayName(true, TimeZone.SHORT, Locale.UK));
-
- // The RI behavior mentioned above does not appear to be because "PST" is a legacy
- // three-character timezone supported by the RI: it happens for "Asia/Tehran"/"IRST" too
- // (IRST is not a legacy code). The RI may just use a different dataset that has "PST" /
- // "IRST" as valid translations (even for scripts like Chinese).
- TimeZone iranTz = TimeZone.getTimeZone("Asia/Tehran");
- assertEquals("Iran Summer Time", iranTz.getDisplayName(true, TimeZone.LONG, Locale.UK));
- assertEquals("Iran Daylight Time", iranTz.getDisplayName(true, TimeZone.LONG, Locale.US));
- assertEquals("Iran Standard Time", iranTz.getDisplayName(false, TimeZone.LONG, Locale.UK));
- assertEquals("Iran Standard Time", iranTz.getDisplayName(false, TimeZone.LONG, Locale.US));
- assertEquals("GMT+03:30", iranTz.getDisplayName(false, TimeZone.SHORT, Locale.UK));
- assertEquals("GMT+04:30", iranTz.getDisplayName(true, TimeZone.SHORT, Locale.UK));
}
public void test_getID() {
diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index 1ca950c..68e9109 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -73,6 +73,12 @@ public class TimeZoneTest extends TestCase {
assertFalse(tz.inDaylightTime(date));
}
+ public void testGetDisplayNameShort_nonHourOffsets() {
+ TimeZone iranTz = TimeZone.getTimeZone("Asia/Tehran");
+ assertEquals("GMT+03:30", iranTz.getDisplayName(false, TimeZone.SHORT, Locale.UK));
+ assertEquals("GMT+04:30", iranTz.getDisplayName(true, TimeZone.SHORT, Locale.UK));
+ }
+
public void testPreHistoricOffsets() throws Exception {
// "Africa/Bissau" has just a few transitions and hasn't changed in a long time.
// 1912-01-01 00:02:19-0100 ... 1912-01-01 00:02:20-0100
@@ -256,12 +262,12 @@ public class TimeZoneTest extends TestCase {
}
// http://b/7955614
- public void test_getDisplayName_GMT_short_names() throws Exception {
- TimeZone tz = TimeZone.getTimeZone("America/Santiago");
- assertEquals("Chile Summer Time", tz.getDisplayName(true, TimeZone.LONG, Locale.US));
- assertEquals("Chile Standard Time", tz.getDisplayName(false, TimeZone.LONG, Locale.US));
- assertEquals("GMT-03:00", tz.getDisplayName(true, TimeZone.SHORT, Locale.US));
- assertEquals("GMT-03:00", tz.getDisplayName(false, TimeZone.SHORT, Locale.US));
+ public void testApia() throws Exception {
+ TimeZone tz = TimeZone.getTimeZone("Pacific/Apia");
+ assertEquals("Apia Daylight Time", tz.getDisplayName(true, TimeZone.LONG, Locale.US));
+ assertEquals("Apia Standard Time", tz.getDisplayName(false, TimeZone.LONG, Locale.US));
+ assertEquals("GMT+14:00", tz.getDisplayName(true, TimeZone.SHORT, Locale.US));
+ assertEquals("GMT+13:00", tz.getDisplayName(false, TimeZone.SHORT, Locale.US));
}
private static boolean isGmtString(String s) {
@@ -290,4 +296,32 @@ public class TimeZoneTest extends TestCase {
}
}
}
+
+ // http://b/18839557
+ public void testOverflowing32BitUnixDates() {
+ final TimeZone tz = TimeZone.getTimeZone("America/New_York");
+
+ // This timezone didn't have any daylight savings prior to 1917 and this
+ // date is sometime in 1901.
+ assertFalse(tz.inDaylightTime(new Date(-2206292400000L)));
+ assertEquals(-18000000, tz.getOffset(-2206292400000L));
+
+ // Nov 30th 2039, no daylight savings as per current rules.
+ assertFalse(tz.inDaylightTime(new Date(2206292400000L)));
+ assertEquals(-18000000, tz.getOffset(2206292400000L));
+ }
+
+ public void testTimeZoneIDLocalization() {
+ Locale defaultLocale = Locale.getDefault();
+ try {
+ Locale.setDefault(new Locale("en"));
+ TimeZone en_timezone = TimeZone.getTimeZone("GMT+09:00");
+ Locale.setDefault(new Locale("ar"));
+ TimeZone ar_timezone = TimeZone.getTimeZone("GMT+09:00");
+
+ assertEquals(en_timezone.getID(), ar_timezone.getID());
+ } finally {
+ Locale.setDefault(defaultLocale);
+ }
+ }
}
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 e5a6cd8..9496ad0 100644
--- a/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java
+++ b/luni/src/test/java/libcore/java/util/jar/StrictJarFileTest.java
@@ -169,6 +169,10 @@ public class StrictJarFileTest extends TestCase {
assertThrowsOnInit("Modified_SF_EntryAttributes.jar");
}
+ public void testJarSigning_removedEntry() throws Exception {
+ assertThrowsOnInit("removed.jar");
+ }
+
private void assertThrowsOnInit(String name) throws Exception {
Support_Resources.copyFile(resources, null, name);
try {
diff --git a/luni/src/test/java/libcore/java/util/zip/AbstractZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/AbstractZipFileTest.java
new file mode 100644
index 0000000..9e049c0
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/zip/AbstractZipFileTest.java
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.util.zip;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+import junit.framework.TestCase;
+
+import tests.support.resource.Support_Resources;
+
+public abstract class AbstractZipFileTest extends TestCase {
+ /**
+ * Exercise Inflater's ability to refill the zlib's input buffer. As of this
+ * writing, this buffer's max size is 64KiB compressed bytes. We'll write a
+ * full megabyte of uncompressed data, which should be sufficient to exhaust
+ * the buffer. http://b/issue?id=2734751
+ */
+ public void testInflatingFilesRequiringZipRefill() throws IOException {
+ int originalSize = 1024 * 1024;
+ byte[] readBuffer = new byte[8192];
+ final File f = createTemporaryZipFile();
+ writeEntries(createZipOutputStream(f), 1, originalSize, false /* setEntrySize */);
+ ZipFile zipFile = new ZipFile(f);
+ for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+ ZipEntry zipEntry = e.nextElement();
+ assertTrue("This test needs >64 KiB of compressed data to exercise Inflater",
+ zipEntry.getCompressedSize() > (64 * 1024));
+ InputStream is = zipFile.getInputStream(zipEntry);
+ while (is.read(readBuffer, 0, readBuffer.length) != -1) {}
+ is.close();
+ }
+ zipFile.close();
+ }
+
+ private static void replaceBytes(byte[] buffer, byte[] original, byte[] replacement) {
+ // Gotcha here: original and replacement must be the same length
+ assertEquals(original.length, replacement.length);
+ boolean found;
+ for(int i=0; i < buffer.length - original.length; i++) {
+ found = false;
+ if (buffer[i] == original[0]) {
+ found = true;
+ for (int j=0; j < original.length; j++) {
+ if (buffer[i+j] != original[j]) {
+ found = false;
+ break;
+ }
+ }
+ }
+ if (found) {
+ for (int j=0; j < original.length; j++) {
+ buffer[i+j] = replacement[j];
+ }
+ }
+ }
+ }
+
+ private static void writeBytes(File f, byte[] bytes) throws IOException {
+ FileOutputStream out = new FileOutputStream(f);
+ out.write(bytes);
+ out.close();
+ }
+
+ /**
+ * Make sure we don't fail silently for duplicate entries.
+ * b/8219321
+ */
+ public void testDuplicateEntries() throws Exception {
+ String name1 = "test_file_name1";
+ String name2 = "test_file_name2";
+
+ // Create the good zip file.
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ZipOutputStream out = createZipOutputStream(baos);
+ out.putNextEntry(new ZipEntry(name2));
+ out.closeEntry();
+ out.putNextEntry(new ZipEntry(name1));
+ out.closeEntry();
+ out.close();
+
+ // Rewrite one of the filenames.
+ byte[] buffer = baos.toByteArray();
+ replaceBytes(buffer, name2.getBytes(), name1.getBytes());
+
+ // Write the result to a file.
+ File badZip = createTemporaryZipFile();
+ writeBytes(badZip, buffer);
+
+ // Check that we refuse to load the modified file.
+ try {
+ ZipFile bad = new ZipFile(badZip);
+ fail();
+ } catch (ZipException expected) {
+ }
+ }
+
+ /**
+ * Make sure the size used for stored zip entires is the uncompressed size.
+ * b/10227498
+ */
+ public void testStoredEntrySize() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ZipOutputStream out = createZipOutputStream(baos);
+
+ // Set up a single stored entry.
+ String name = "test_file";
+ int expectedLength = 5;
+ ZipEntry outEntry = new ZipEntry(name);
+ byte[] buffer = new byte[expectedLength];
+ outEntry.setMethod(ZipEntry.STORED);
+ CRC32 crc = new CRC32();
+ crc.update(buffer);
+ outEntry.setCrc(crc.getValue());
+ outEntry.setSize(buffer.length);
+
+ out.putNextEntry(outEntry);
+ out.write(buffer);
+ out.closeEntry();
+ out.close();
+
+ // Write the result to a file.
+ byte[] outBuffer = baos.toByteArray();
+ File zipFile = createTemporaryZipFile();
+ writeBytes(zipFile, outBuffer);
+
+ ZipFile zip = new ZipFile(zipFile);
+ // Set up the zip entry to have different compressed/uncompressed sizes.
+ ZipEntry ze = zip.getEntry(name);
+ ze.setCompressedSize(expectedLength - 1);
+ // Read the contents of the stream and verify uncompressed size was used.
+ InputStream stream = zip.getInputStream(ze);
+ int count = 0;
+ int read;
+ while ((read = stream.read(buffer)) != -1) {
+ count += read;
+ }
+
+ assertEquals(expectedLength, count);
+ zip.close();
+ }
+
+ public void testInflatingStreamsRequiringZipRefill() throws IOException {
+ int originalSize = 1024 * 1024;
+ byte[] readBuffer = new byte[8192];
+ final File f = createTemporaryZipFile();
+ writeEntries(createZipOutputStream(f), 1, originalSize, false /* setEntrySize */);
+
+ ZipInputStream in = new ZipInputStream(new FileInputStream(f));
+ while (in.getNextEntry() != null) {
+ while (in.read(readBuffer, 0, readBuffer.length) != -1) {}
+ }
+ in.close();
+ }
+
+ public void testZipFileWithLotsOfEntries() throws IOException {
+ int expectedEntryCount = 64*1024 - 1;
+ final File f = createTemporaryZipFile();
+ writeEntries(createZipOutputStream(f), expectedEntryCount, 0, false /* setEntrySize */);
+ ZipFile zipFile = new ZipFile(f);
+ int entryCount = 0;
+ for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+ ZipEntry zipEntry = e.nextElement();
+ ++entryCount;
+ }
+ assertEquals(expectedEntryCount, entryCount);
+ zipFile.close();
+ }
+
+ // http://code.google.com/p/android/issues/detail?id=36187
+ public void testZipFileLargerThan2GiB() throws IOException {
+ if (false) { // TODO: this test requires too much time and too much disk space!
+ final File f = createTemporaryZipFile();
+ writeEntries(createZipOutputStream(f), 1024, 3*1024*1024, false /* setEntrySize */);
+ ZipFile zipFile = new ZipFile(f);
+ int entryCount = 0;
+ for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+ e.nextElement();
+ ++entryCount;
+ }
+ assertEquals(1024, entryCount);
+ zipFile.close();
+ }
+ }
+
+ /**
+ * Compresses the given number of files, each of the given size, into a .zip archive.
+ */
+ protected void writeEntries(ZipOutputStream out, int entryCount, long entrySize,
+ boolean setEntrySize)
+ throws IOException {
+ byte[] writeBuffer = new byte[8192];
+ Random random = new Random();
+ try {
+ for (int entry = 0; entry < entryCount; ++entry) {
+ ZipEntry ze = new ZipEntry(Integer.toHexString(entry));
+ if (setEntrySize) {
+ ze.setSize(entrySize);
+ }
+ out.putNextEntry(ze);
+
+ for (long i = 0; i < entrySize; i += writeBuffer.length) {
+ random.nextBytes(writeBuffer);
+ int byteCount = (int) Math.min(writeBuffer.length, entrySize - i);
+ out.write(writeBuffer, 0, byteCount);
+ }
+
+ out.closeEntry();
+ }
+ } finally {
+ out.close();
+ }
+ }
+
+ static File createTemporaryZipFile() throws IOException {
+ File result = File.createTempFile("ZipFileTest", ".zip");
+ result.deleteOnExit();
+ return result;
+ }
+
+ private ZipOutputStream createZipOutputStream(File f) throws IOException {
+ return createZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
+ }
+
+ protected abstract ZipOutputStream createZipOutputStream(OutputStream wrapped);
+
+ public void testSTORED() throws IOException {
+ ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
+ CRC32 crc = new CRC32();
+
+ // Missing CRC, size, and compressed size => failure.
+ try {
+ ZipEntry ze = new ZipEntry("a");
+ ze.setMethod(ZipEntry.STORED);
+ out.putNextEntry(ze);
+ fail();
+ } catch (ZipException expected) {
+ }
+
+ // Missing CRC and compressed size => failure.
+ try {
+ ZipEntry ze = new ZipEntry("a");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setSize(0);
+ out.putNextEntry(ze);
+ fail();
+ } catch (ZipException expected) {
+ }
+
+ // Missing CRC and size => failure.
+ try {
+ ZipEntry ze = new ZipEntry("a");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setSize(0);
+ ze.setCompressedSize(0);
+ out.putNextEntry(ze);
+ fail();
+ } catch (ZipException expected) {
+ }
+
+ // Missing size and compressed size => failure.
+ try {
+ ZipEntry ze = new ZipEntry("a");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setCrc(crc.getValue());
+ out.putNextEntry(ze);
+ fail();
+ } catch (ZipException expected) {
+ }
+
+ // Missing size is copied from compressed size.
+ {
+ ZipEntry ze = new ZipEntry("okay1");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setCrc(crc.getValue());
+
+ assertEquals(-1, ze.getSize());
+ assertEquals(-1, ze.getCompressedSize());
+
+ ze.setCompressedSize(0);
+
+ assertEquals(-1, ze.getSize());
+ assertEquals(0, ze.getCompressedSize());
+
+ out.putNextEntry(ze);
+
+ assertEquals(0, ze.getSize());
+ assertEquals(0, ze.getCompressedSize());
+ }
+
+ // Missing compressed size is copied from size.
+ {
+ ZipEntry ze = new ZipEntry("okay2");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setCrc(crc.getValue());
+
+ assertEquals(-1, ze.getSize());
+ assertEquals(-1, ze.getCompressedSize());
+
+ ze.setSize(0);
+
+ assertEquals(0, ze.getSize());
+ assertEquals(-1, ze.getCompressedSize());
+
+ out.putNextEntry(ze);
+
+ assertEquals(0, ze.getSize());
+ assertEquals(0, ze.getCompressedSize());
+ }
+
+ // Mismatched size and compressed size => failure.
+ try {
+ ZipEntry ze = new ZipEntry("a");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setCrc(crc.getValue());
+ ze.setCompressedSize(1);
+ ze.setSize(0);
+ out.putNextEntry(ze);
+ fail();
+ } catch (ZipException expected) {
+ }
+
+ // Everything present => success.
+ ZipEntry ze = new ZipEntry("okay");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setCrc(crc.getValue());
+ ze.setSize(0);
+ ze.setCompressedSize(0);
+ out.putNextEntry(ze);
+
+ out.close();
+ }
+
+ private String makeString(int count, String ch) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < count; ++i) {
+ sb.append(ch);
+ }
+ return sb.toString();
+ }
+
+ public void testComments() throws Exception {
+ String expectedFileComment = "1 \u0666 2";
+ String expectedEntryComment = "a \u0666 b";
+
+ File file = createTemporaryZipFile();
+ ZipOutputStream out = createZipOutputStream(file);
+
+ // Is file comment length checking done on bytes or characters? (Should be bytes.)
+ out.setComment(null);
+ out.setComment(makeString(0xffff, "a"));
+ try {
+ out.setComment(makeString(0xffff + 1, "a"));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ out.setComment(makeString(0xffff, "\u0666"));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ ZipEntry ze = new ZipEntry("a");
+
+ // Is entry comment length checking done on bytes or characters? (Should be bytes.)
+ ze.setComment(null);
+ ze.setComment(makeString(0xffff, "a"));
+ try {
+ ze.setComment(makeString(0xffff + 1, "a"));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ ze.setComment(makeString(0xffff, "\u0666"));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ ze.setComment(expectedEntryComment);
+ out.putNextEntry(ze);
+ out.closeEntry();
+
+ out.setComment(expectedFileComment);
+ out.close();
+
+ ZipFile zipFile = new ZipFile(file);
+ assertEquals(expectedFileComment, zipFile.getComment());
+ assertEquals(expectedEntryComment, zipFile.getEntry("a").getComment());
+ zipFile.close();
+ }
+
+ public void test_getComment_unset() throws Exception {
+ File file = createTemporaryZipFile();
+ ZipOutputStream out = createZipOutputStream(file);
+ ZipEntry ze = new ZipEntry("test entry");
+ ze.setComment("per-entry comment");
+ out.putNextEntry(ze);
+ out.close();
+
+ ZipFile zipFile = new ZipFile(file);
+ assertEquals(null, zipFile.getComment());
+ }
+
+ // https://code.google.com/p/android/issues/detail?id=58465
+ public void test_NUL_in_filename() throws Exception {
+ File file = createTemporaryZipFile();
+
+ // We allow creation of a ZipEntry whose name contains a NUL byte,
+ // mainly because it's not likely to happen by accident and it's useful for testing.
+ ZipOutputStream out = createZipOutputStream(file);
+ out.putNextEntry(new ZipEntry("hello"));
+ out.putNextEntry(new ZipEntry("hello\u0000"));
+ out.close();
+
+ // But you can't open a ZIP file containing such an entry, because we reject it
+ // when we find it in the central directory.
+ try {
+ ZipFile zipFile = new ZipFile(file);
+ fail();
+ } catch (ZipException expected) {
+ }
+ }
+
+ public void testCrc() throws IOException {
+ ZipEntry ze = new ZipEntry("test");
+ ze.setMethod(ZipEntry.STORED);
+ ze.setSize(4);
+
+ // setCrc takes a long, not an int, so -1 isn't a valid CRC32 (because it's 64 bits).
+ try {
+ ze.setCrc(-1);
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // You can set the CRC32 to 0xffffffff if you're slightly more careful though...
+ ze.setCrc(0xffffffffL);
+ assertEquals(0xffffffffL, ze.getCrc());
+
+ // And it actually works, even though we use -1L to mean "no CRC set"...
+ ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
+ out.putNextEntry(ze);
+ out.write(-1);
+ out.write(-1);
+ out.write(-1);
+ out.write(-1);
+ out.closeEntry();
+ out.close();
+ }
+
+ /**
+ * RI does not allow reading of an empty zip using a {@link ZipFile}.
+ */
+ public void testConstructorFailsWhenReadingEmptyZipArchive() throws IOException {
+
+ File resources = Support_Resources.createTempFolder();
+ File emptyZip = Support_Resources.copyFile(
+ resources, "java/util/zip", "EmptyArchive.zip");
+
+ try {
+ // The following should fail with an exception but if it doesn't then we need to clean
+ // up the resource so we need a reference to it.
+ ZipFile zipFile = new ZipFile(emptyZip);
+
+ // Clean up the resource.
+ try {
+ zipFile.close();
+ } catch (Exception e) {
+ // Ignore
+ }
+ fail();
+ } catch (ZipException expected) {
+ // expected
+ }
+ }
+
+ // Demonstrates http://b/18644314 : Zip entry names are relative to the point of
+ // extraction and can contain relative paths "../" and "./".
+ //
+ // It is left to callers of the API to perform any validation / santization to
+ // ensure that files are not written outside of the destination directory, where that
+ // is a concern.
+ public void testArchivesWithRelativePaths() throws IOException {
+ String[] entryNames = {
+ "../",
+ "../foo.bar",
+ "foo/../../",
+ "foo/../../bar.baz"
+ };
+
+ File zip = createTemporaryZipFile();
+ ZipOutputStream out = createZipOutputStream(zip);
+
+ try {
+ byte[] entryData = new byte[1024];
+ for (String entryName : entryNames) {
+ ZipEntry ze = new ZipEntry(entryName);
+ out.putNextEntry(ze);
+ out.write(entryData);
+ out.closeEntry();
+ }
+ } finally {
+ out.close();
+ }
+
+ ZipFile zf = new ZipFile(zip, ZipFile.OPEN_READ);
+ Enumeration<? extends ZipEntry> entries = zf.entries();
+ Set<String> entryNamesFromFile = new HashSet<>();
+ while (entries.hasMoreElements()) {
+ ZipEntry ze = entries.nextElement();
+ entryNamesFromFile.add(ze.getName());
+ }
+
+ zf.close();
+
+ for (String entryName : entryNames) {
+ assertTrue(entryNamesFromFile.contains(entryName));
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java b/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java
index 30aa7f3..1dfa775 100644
--- a/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/DeflaterTest.java
@@ -82,4 +82,26 @@ public class DeflaterTest extends TestCase {
assertTrue(totalDeflated > 0); // the deflated form should be non-empty
assertEquals(0, totalInflated);
}
+
+ public void testDeflaterCounts() throws Exception {
+ deflater.setInput(new byte[] { 1, 2, 3 });
+ assertEquals(11, deflater.deflate(compressed, 0, compressed.length, Deflater.FULL_FLUSH));
+ assertEquals(3, deflater.getBytesRead());
+ assertEquals(3, deflater.getTotalIn());
+ assertEquals(11, deflater.getBytesWritten());
+ assertEquals(11, deflater.getTotalOut());
+
+ deflater.setInput(new byte[] { 1, 2, 3 });
+ assertEquals(9, deflater.deflate(compressed, 0, compressed.length, Deflater.FULL_FLUSH));
+ assertEquals(6, deflater.getBytesRead());
+ assertEquals(6, deflater.getTotalIn());
+ assertEquals(20, deflater.getBytesWritten());
+
+
+ deflater.reset();
+ assertEquals(0, deflater.getBytesRead());
+ assertEquals(0, deflater.getBytesWritten());
+ assertEquals(0, deflater.getTotalIn());
+ assertEquals(0, deflater.getTotalOut());
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
index 494520a..5813753 100644
--- a/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
@@ -35,14 +35,46 @@ import libcore.io.Streams;
public final class GZIPInputStreamTest extends TestCase {
private static final byte[] HELLO_WORLD_GZIPPED = new byte[] {
- 31, -117, 8, 0, 0, 0, 0, 0, 0, 0, -13, 72, -51, -55, -55, 87, 8, -49,
- 47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0
+ 31, -117, 8, 0, 0, 0, 0, 0, 0, 0, // 10 byte header
+ -13, 72, -51, -55, -55, 87, 8, -49, 47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0 // data
+ };
+
+ /**
+ * This is the same as the above, except that the 4th header byte is 2 (FHCRC flag)
+ * and the 2 bytes after the header make up the CRC.
+ *
+ * Constructed manually because none of the commonly used tools appear to emit header CRCs.
+ */
+ private static final byte[] HELLO_WORLD_GZIPPED_WITH_HEADER_CRC = new byte[] {
+ 31, -117, 8, 2, 0, 0, 0, 0, 0, 0, // 10 byte header
+ 29, 38, // 2 byte CRC.
+ -13, 72, -51, -55, -55, 87, 8, -49, 47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0 // data
+ };
+
+ /*(
+ * This is the same as {@code HELLO_WORLD_GZIPPED} except that the 4th header byte is 4
+ * (FEXTRA flag) and that the 8 bytes after the header make up the extra.
+ *
+ * Constructed manually because none of the commonly used tools appear to emit header CRCs.
+ */
+ private static final byte[] HELLO_WORLD_GZIPPED_WITH_EXTRA = new byte[] {
+ 31, -117, 8, 4, 0, 0, 0, 0, 0, 0, // 10 byte header
+ 6, 0, 4, 2, 4, 2, 4, 2, // 2 byte extra length + 6 byte extra.
+ -13, 72, -51, -55, -55, 87, 8, -49, 47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0 // data
};
public void testShortMessage() throws IOException {
assertEquals("Hello World", new String(gunzip(HELLO_WORLD_GZIPPED), "UTF-8"));
}
+ public void testShortMessageWithCrc() throws IOException {
+ assertEquals("Hello World", new String(gunzip(HELLO_WORLD_GZIPPED_WITH_HEADER_CRC), "UTF-8"));
+ }
+
+ public void testShortMessageWithHeaderExtra() throws IOException {
+ assertEquals("Hello World", new String(gunzip(HELLO_WORLD_GZIPPED_WITH_EXTRA), "UTF-8"));
+ }
+
public void testLongMessage() throws IOException {
byte[] data = new byte[1024 * 1024];
new Random().nextBytes(data);
diff --git a/luni/src/test/java/libcore/java/util/zip/InflaterTest.java b/luni/src/test/java/libcore/java/util/zip/InflaterTest.java
index 158b3e9..cce08f3 100644
--- a/luni/src/test/java/libcore/java/util/zip/InflaterTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/InflaterTest.java
@@ -122,4 +122,35 @@ public class InflaterTest extends TestCase {
adler32.update(bytes);
return (int) adler32.getValue();
}
+
+ public void testInflaterCounts() throws Exception {
+ Inflater inflater = new Inflater();
+
+ byte[] decompressed = new byte[32];
+ byte[] compressed = deflate(new byte[] { 1, 2, 3}, null);
+ assertEquals(11, compressed.length);
+
+ // Feed in bytes [0, 5) to the first iteration.
+ inflater.setInput(compressed, 0, 5);
+ inflater.inflate(decompressed, 0, decompressed.length);
+ assertEquals(5, inflater.getBytesRead());
+ assertEquals(5, inflater.getTotalIn());
+ assertEquals(2, inflater.getBytesWritten());
+ assertEquals(2, inflater.getTotalOut());
+
+ // Feed in bytes [5, 11) to the second iteration.
+ assertEquals(true, inflater.needsInput());
+ inflater.setInput(compressed, 5, 6);
+ assertEquals(1, inflater.inflate(decompressed, 0, decompressed.length));
+ assertEquals(11, inflater.getBytesRead());
+ assertEquals(11, inflater.getTotalIn());
+ assertEquals(3, inflater.getBytesWritten());
+ assertEquals(3, inflater.getTotalOut());
+
+ inflater.reset();
+ assertEquals(0, inflater.getBytesRead());
+ assertEquals(0, inflater.getTotalIn());
+ assertEquals(0, inflater.getBytesWritten());
+ assertEquals(0, inflater.getTotalOut());
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/zip/Zip64FileTest.java b/luni/src/test/java/libcore/java/util/zip/Zip64FileTest.java
new file mode 100644
index 0000000..1b733f9
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/zip/Zip64FileTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.zip;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+public final class Zip64FileTest extends AbstractZipFileTest {
+ @Override
+ protected ZipOutputStream createZipOutputStream(OutputStream wrapped) {
+ return new ZipOutputStream(wrapped, true /* forceZip64 */);
+ }
+
+ public void testZip64Support_largeNumberOfEntries() throws IOException {
+ final File file = createZipFile(65550, 2, false /* setEntrySize */);
+ ZipFile zf = null;
+ try {
+ zf = new ZipFile(file);
+ assertEquals(65550, zf.size());
+
+ Enumeration<? extends ZipEntry> entries = zf.entries();
+ assertTrue(entries.hasMoreElements());
+ ZipEntry ze = entries.nextElement();
+ assertEquals(2, ze.getSize());
+ } finally {
+ if (zf != null) {
+ zf.close();
+ }
+ }
+ }
+
+ public void testZip64Support_totalLargerThan4G() throws IOException {
+ final File file = createZipFile(5, 1073741824L, false /* setEntrySize */);
+ ZipFile zf = null;
+ try {
+ zf = new ZipFile(file);
+ assertEquals(5, zf.size());
+ Enumeration<? extends ZipEntry> entries = zf.entries();
+ assertTrue(entries.hasMoreElements());
+ ZipEntry ze = entries.nextElement();
+ assertEquals(1073741824L, ze.getSize());
+ } finally {
+ if (zf != null) {
+ zf.close();
+ }
+ }
+ }
+
+ public void testZip64Support_hugeEntry() throws IOException {
+ try {
+ createZipFile(1, 4294967410L, false /* setEntrySize */);
+ fail();
+ } catch (IOException expected) {
+ }
+
+ final File file = createZipFile(1, 4294967410L, true /* setEntrySize */);
+ ZipFile zf = null;
+ try {
+ zf = new ZipFile(file);
+ assertEquals(1, zf.size());
+ Enumeration<? extends ZipEntry> entries = zf.entries();
+ assertTrue(entries.hasMoreElements());
+ ZipEntry ze = entries.nextElement();
+ assertEquals(4294967410L, ze.getSize());
+ } finally {
+ if (zf != null) {
+ zf.close();
+ }
+ }
+ }
+
+ private File createZipFile(int numEntries, long entrySize, boolean setEntrySize)
+ throws IOException {
+ File file = createTemporaryZipFile();
+ // Don't force a 64 bit zip file to test that our heuristics work.
+ ZipOutputStream os = new ZipOutputStream(
+ new BufferedOutputStream(new FileOutputStream(file)));
+ writeEntries(os, numEntries, entrySize, setEntrySize);
+ return file;
+ }
+}
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java b/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java
index 550ddfb..c7667b9 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipEntryTest.java
@@ -25,6 +25,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
@@ -132,6 +133,7 @@ public class ZipEntryTest extends junit.framework.TestCase {
File f = createTemporaryZipFile();
ZipOutputStream out = createZipOutputStream(f);
ZipEntry ze = new ZipEntry("x");
+ ze.setSize(0);
ze.setExtra(maxLengthExtra);
out.putNextEntry(ze);
out.closeEntry();
@@ -143,6 +145,25 @@ public class ZipEntryTest extends junit.framework.TestCase {
zipFile.close();
}
+ public void testMaxLengthExtra_zip64() throws Exception {
+ // Not quite the max length (65535), but large enough that there's no space
+ // for the zip64 extended info header.
+ byte[] maxLengthExtra = new byte[65530];
+
+ File f = createTemporaryZipFile();
+ ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)),
+ true /* forceZip64 */);
+ ZipEntry ze = new ZipEntry("x");
+
+ ze.setExtra(maxLengthExtra);
+ try {
+ out.putNextEntry(ze);
+ fail();
+ } catch (ZipException expected) {
+ }
+ }
+
+
public void testTooLongComment() throws Exception {
String tooLongComment = makeString(65536, "z");
ZipEntry ze = new ZipEntry("x");
@@ -176,7 +197,17 @@ public class ZipEntryTest extends junit.framework.TestCase {
File f = createTemporaryZipFile();
ZipOutputStream out = createZipOutputStream(f);
+
+ // Regular (non zip64) format.
ZipEntry ze = new ZipEntry("x");
+ ze.setSize(0);
+ ze.setExtra(extra);
+ ze.setComment(comment);
+ out.putNextEntry(ze);
+ out.closeEntry();
+
+ // An entry without a length is assumed to be zip64.
+ ze = new ZipEntry("y");
ze.setExtra(extra);
ze.setComment(comment);
out.putNextEntry(ze);
@@ -188,6 +219,9 @@ public class ZipEntryTest extends junit.framework.TestCase {
try {
assertEquals(comment, zipFile.getEntry("x").getComment());
assertTrue(Arrays.equals(extra, zipFile.getEntry("x").getExtra()));
+
+ assertEquals(comment, zipFile.getEntry("y").getComment());
+ assertTrue(Arrays.equals(extra, zipFile.getEntry("y").getExtra()));
} finally {
zipFile.close();
}
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
index a9ff56f..02210ac 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,497 +11,18 @@
* 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.
+ * limitations under the License
*/
package libcore.java.util.zip;
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-import java.util.zip.CRC32;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipException;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
-import junit.framework.TestCase;
-import tests.support.resource.Support_Resources;
+public final class ZipFileTest extends AbstractZipFileTest {
-public final class ZipFileTest extends TestCase {
- /**
- * Exercise Inflater's ability to refill the zlib's input buffer. As of this
- * writing, this buffer's max size is 64KiB compressed bytes. We'll write a
- * full megabyte of uncompressed data, which should be sufficient to exhaust
- * the buffer. http://b/issue?id=2734751
- */
- public void testInflatingFilesRequiringZipRefill() throws IOException {
- int originalSize = 1024 * 1024;
- byte[] readBuffer = new byte[8192];
- ZipFile zipFile = new ZipFile(createZipFile(1, originalSize));
- for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
- ZipEntry zipEntry = e.nextElement();
- assertTrue("This test needs >64 KiB of compressed data to exercise Inflater",
- zipEntry.getCompressedSize() > (64 * 1024));
- InputStream is = zipFile.getInputStream(zipEntry);
- while (is.read(readBuffer, 0, readBuffer.length) != -1) {}
- is.close();
- }
- zipFile.close();
- }
-
- private static void replaceBytes(byte[] buffer, byte[] original, byte[] replacement) {
- // Gotcha here: original and replacement must be the same length
- assertEquals(original.length, replacement.length);
- boolean found;
- for(int i=0; i < buffer.length - original.length; i++) {
- found = false;
- if (buffer[i] == original[0]) {
- found = true;
- for (int j=0; j < original.length; j++) {
- if (buffer[i+j] != original[j]) {
- found = false;
- break;
- }
- }
- }
- if (found) {
- for (int j=0; j < original.length; j++) {
- buffer[i+j] = replacement[j];
- }
- }
- }
- }
-
- private static void writeBytes(File f, byte[] bytes) throws IOException {
- FileOutputStream out = new FileOutputStream(f);
- out.write(bytes);
- out.close();
- }
-
- /**
- * Make sure we don't fail silently for duplicate entries.
- * b/8219321
- */
- public void testDuplicateEntries() throws Exception {
- String name1 = "test_file_name1";
- String name2 = "test_file_name2";
-
- // Create the good zip file.
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ZipOutputStream out = new ZipOutputStream(baos);
- out.putNextEntry(new ZipEntry(name2));
- out.closeEntry();
- out.putNextEntry(new ZipEntry(name1));
- out.closeEntry();
- out.close();
-
- // Rewrite one of the filenames.
- byte[] buffer = baos.toByteArray();
- replaceBytes(buffer, name2.getBytes(), name1.getBytes());
-
- // Write the result to a file.
- File badZip = createTemporaryZipFile();
- writeBytes(badZip, buffer);
-
- // Check that we refuse to load the modified file.
- try {
- ZipFile bad = new ZipFile(badZip);
- fail();
- } catch (ZipException expected) {
- }
- }
-
- /**
- * Make sure the size used for stored zip entires is the uncompressed size.
- * b/10227498
- */
- public void testStoredEntrySize() throws Exception {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ZipOutputStream out = new ZipOutputStream(baos);
-
- // Set up a single stored entry.
- String name = "test_file";
- int expectedLength = 5;
- ZipEntry outEntry = new ZipEntry(name);
- byte[] buffer = new byte[expectedLength];
- outEntry.setMethod(ZipEntry.STORED);
- CRC32 crc = new CRC32();
- crc.update(buffer);
- outEntry.setCrc(crc.getValue());
- outEntry.setSize(buffer.length);
-
- out.putNextEntry(outEntry);
- out.write(buffer);
- out.closeEntry();
- out.close();
-
- // Write the result to a file.
- byte[] outBuffer = baos.toByteArray();
- File zipFile = createTemporaryZipFile();
- writeBytes(zipFile, outBuffer);
-
- ZipFile zip = new ZipFile(zipFile);
- // Set up the zip entry to have different compressed/uncompressed sizes.
- ZipEntry ze = zip.getEntry(name);
- ze.setCompressedSize(expectedLength - 1);
- // Read the contents of the stream and verify uncompressed size was used.
- InputStream stream = zip.getInputStream(ze);
- int count = 0;
- int read;
- while ((read = stream.read(buffer)) != -1) {
- count += read;
- }
-
- assertEquals(expectedLength, count);
- zip.close();
- }
-
- public void testInflatingStreamsRequiringZipRefill() throws IOException {
- int originalSize = 1024 * 1024;
- byte[] readBuffer = new byte[8192];
- ZipInputStream in = new ZipInputStream(new FileInputStream(createZipFile(1, originalSize)));
- while (in.getNextEntry() != null) {
- while (in.read(readBuffer, 0, readBuffer.length) != -1) {}
- }
- in.close();
- }
-
- public void testZipFileWithLotsOfEntries() throws IOException {
- int expectedEntryCount = 64*1024 - 1;
- File f = createZipFile(expectedEntryCount, 0);
- ZipFile zipFile = new ZipFile(f);
- int entryCount = 0;
- for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
- ZipEntry zipEntry = e.nextElement();
- ++entryCount;
- }
- assertEquals(expectedEntryCount, entryCount);
- zipFile.close();
- }
-
- // http://code.google.com/p/android/issues/detail?id=36187
- public void testZipFileLargerThan2GiB() throws IOException {
- if (false) { // TODO: this test requires too much time and too much disk space!
- File f = createZipFile(1024, 3*1024*1024);
- ZipFile zipFile = new ZipFile(f);
- int entryCount = 0;
- for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
- ZipEntry zipEntry = e.nextElement();
- ++entryCount;
- }
- assertEquals(1024, entryCount);
- zipFile.close();
- }
- }
-
- public void testZip64Support() throws IOException {
- try {
- createZipFile(64*1024, 0);
- fail(); // Make this test more like testHugeZipFile when we have Zip64 support.
- } catch (ZipException expected) {
- }
- }
-
- /**
- * Compresses the given number of files, each of the given size, into a .zip archive.
- */
- private File createZipFile(int entryCount, int entrySize) throws IOException {
- File result = createTemporaryZipFile();
-
- byte[] writeBuffer = new byte[8192];
- Random random = new Random();
-
- ZipOutputStream out = createZipOutputStream(result);
- try {
- for (int entry = 0; entry < entryCount; ++entry) {
- ZipEntry ze = new ZipEntry(Integer.toHexString(entry));
- out.putNextEntry(ze);
-
- for (int i = 0; i < entrySize; i += writeBuffer.length) {
- random.nextBytes(writeBuffer);
- int byteCount = Math.min(writeBuffer.length, entrySize - i);
- out.write(writeBuffer, 0, byteCount);
- }
-
- out.closeEntry();
- }
- } finally {
- out.close();
- }
- return result;
- }
-
- private File createTemporaryZipFile() throws IOException {
- File result = File.createTempFile("ZipFileTest", "zip");
- result.deleteOnExit();
- return result;
- }
-
- private ZipOutputStream createZipOutputStream(File f) throws IOException {
- return new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
- }
-
- public void testSTORED() throws IOException {
- ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
- CRC32 crc = new CRC32();
-
- // Missing CRC, size, and compressed size => failure.
- try {
- ZipEntry ze = new ZipEntry("a");
- ze.setMethod(ZipEntry.STORED);
- out.putNextEntry(ze);
- fail();
- } catch (ZipException expected) {
- }
-
- // Missing CRC and compressed size => failure.
- try {
- ZipEntry ze = new ZipEntry("a");
- ze.setMethod(ZipEntry.STORED);
- ze.setSize(0);
- out.putNextEntry(ze);
- fail();
- } catch (ZipException expected) {
- }
-
- // Missing CRC and size => failure.
- try {
- ZipEntry ze = new ZipEntry("a");
- ze.setMethod(ZipEntry.STORED);
- ze.setSize(0);
- ze.setCompressedSize(0);
- out.putNextEntry(ze);
- fail();
- } catch (ZipException expected) {
- }
-
- // Missing size and compressed size => failure.
- try {
- ZipEntry ze = new ZipEntry("a");
- ze.setMethod(ZipEntry.STORED);
- ze.setCrc(crc.getValue());
- out.putNextEntry(ze);
- fail();
- } catch (ZipException expected) {
- }
-
- // Missing size is copied from compressed size.
- {
- ZipEntry ze = new ZipEntry("okay1");
- ze.setMethod(ZipEntry.STORED);
- ze.setCrc(crc.getValue());
-
- assertEquals(-1, ze.getSize());
- assertEquals(-1, ze.getCompressedSize());
-
- ze.setCompressedSize(0);
-
- assertEquals(-1, ze.getSize());
- assertEquals(0, ze.getCompressedSize());
-
- out.putNextEntry(ze);
-
- assertEquals(0, ze.getSize());
- assertEquals(0, ze.getCompressedSize());
- }
-
- // Missing compressed size is copied from size.
- {
- ZipEntry ze = new ZipEntry("okay2");
- ze.setMethod(ZipEntry.STORED);
- ze.setCrc(crc.getValue());
-
- assertEquals(-1, ze.getSize());
- assertEquals(-1, ze.getCompressedSize());
-
- ze.setSize(0);
-
- assertEquals(0, ze.getSize());
- assertEquals(-1, ze.getCompressedSize());
-
- out.putNextEntry(ze);
-
- assertEquals(0, ze.getSize());
- assertEquals(0, ze.getCompressedSize());
- }
-
- // Mismatched size and compressed size => failure.
- try {
- ZipEntry ze = new ZipEntry("a");
- ze.setMethod(ZipEntry.STORED);
- ze.setCrc(crc.getValue());
- ze.setCompressedSize(1);
- ze.setSize(0);
- out.putNextEntry(ze);
- fail();
- } catch (ZipException expected) {
- }
-
- // Everything present => success.
- ZipEntry ze = new ZipEntry("okay");
- ze.setMethod(ZipEntry.STORED);
- ze.setCrc(crc.getValue());
- ze.setSize(0);
- ze.setCompressedSize(0);
- out.putNextEntry(ze);
-
- out.close();
- }
-
- private String makeString(int count, String ch) {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < count; ++i) {
- sb.append(ch);
- }
- return sb.toString();
- }
-
- public void testComments() throws Exception {
- String expectedFileComment = "1 \u0666 2";
- String expectedEntryComment = "a \u0666 b";
-
- File file = createTemporaryZipFile();
- ZipOutputStream out = createZipOutputStream(file);
-
- // Is file comment length checking done on bytes or characters? (Should be bytes.)
- out.setComment(null);
- out.setComment(makeString(0xffff, "a"));
- try {
- out.setComment(makeString(0xffff + 1, "a"));
- fail();
- } catch (IllegalArgumentException expected) {
- }
- try {
- out.setComment(makeString(0xffff, "\u0666"));
- fail();
- } catch (IllegalArgumentException expected) {
- }
-
- ZipEntry ze = new ZipEntry("a");
-
- // Is entry comment length checking done on bytes or characters? (Should be bytes.)
- ze.setComment(null);
- ze.setComment(makeString(0xffff, "a"));
- try {
- ze.setComment(makeString(0xffff + 1, "a"));
- fail();
- } catch (IllegalArgumentException expected) {
- }
- try {
- ze.setComment(makeString(0xffff, "\u0666"));
- fail();
- } catch (IllegalArgumentException expected) {
- }
-
- ze.setComment(expectedEntryComment);
- out.putNextEntry(ze);
- out.closeEntry();
-
- out.setComment(expectedFileComment);
- out.close();
-
- ZipFile zipFile = new ZipFile(file);
- assertEquals(expectedFileComment, zipFile.getComment());
- assertEquals(expectedEntryComment, zipFile.getEntry("a").getComment());
- zipFile.close();
- }
-
- public void test_getComment_unset() throws Exception {
- File file = createTemporaryZipFile();
- ZipOutputStream out = createZipOutputStream(file);
- ZipEntry ze = new ZipEntry("test entry");
- ze.setComment("per-entry comment");
- out.putNextEntry(ze);
- out.close();
-
- ZipFile zipFile = new ZipFile(file);
- assertEquals(null, zipFile.getComment());
- }
-
- // https://code.google.com/p/android/issues/detail?id=58465
- public void test_NUL_in_filename() throws Exception {
- File file = createTemporaryZipFile();
-
- // We allow creation of a ZipEntry whose name contains a NUL byte,
- // mainly because it's not likely to happen by accident and it's useful for testing.
- ZipOutputStream out = createZipOutputStream(file);
- out.putNextEntry(new ZipEntry("hello"));
- out.putNextEntry(new ZipEntry("hello\u0000"));
- out.close();
-
- // But you can't open a ZIP file containing such an entry, because we reject it
- // when we find it in the central directory.
- try {
- ZipFile zipFile = new ZipFile(file);
- fail();
- } catch (ZipException expected) {
- }
- }
-
- public void testCrc() throws IOException {
- ZipEntry ze = new ZipEntry("test");
- ze.setMethod(ZipEntry.STORED);
- ze.setSize(4);
-
- // setCrc takes a long, not an int, so -1 isn't a valid CRC32 (because it's 64 bits).
- try {
- ze.setCrc(-1);
- } catch (IllegalArgumentException expected) {
- }
-
- // You can set the CRC32 to 0xffffffff if you're slightly more careful though...
- ze.setCrc(0xffffffffL);
- assertEquals(0xffffffffL, ze.getCrc());
-
- // And it actually works, even though we use -1L to mean "no CRC set"...
- ZipOutputStream out = createZipOutputStream(createTemporaryZipFile());
- out.putNextEntry(ze);
- out.write(-1);
- out.write(-1);
- out.write(-1);
- out.write(-1);
- out.closeEntry();
- out.close();
- }
-
- /**
- * RI does not allow reading of an empty zip using a {@link ZipFile}.
- */
- public void testConstructorFailsWhenReadingEmptyZipArchive() throws IOException {
-
- File resources = Support_Resources.createTempFolder();
- File emptyZip = Support_Resources.copyFile(
- resources, "java/util/zip", "EmptyArchive.zip");
-
- try {
- // The following should fail with an exception but if it doesn't then we need to clean
- // up the resource so we need a reference to it.
- ZipFile zipFile = new ZipFile(emptyZip);
-
- // Clean up the resource.
- try {
- zipFile.close();
- } catch (Exception e) {
- // Ignore
- }
- fail();
- } catch (ZipException expected) {
- // expected
- }
+ @Override
+ protected ZipOutputStream createZipOutputStream(OutputStream wrapped) {
+ return new ZipOutputStream(wrapped);
}
}
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
index e69f010..15600de 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
@@ -21,7 +21,6 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Random;
import java.util.zip.ZipEntry;
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index c89886c..494d15e 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -847,6 +847,24 @@ public final class CipherTest extends TestCase {
}
}
+ public void testCipher_getInstance_DoesNotSupportKeyClass_Success() throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
+ put("Cipher.FOO SupportedKeyClasses", "None");
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ Cipher c = Cipher.getInstance("FOO", mockProvider);
+ c.init(Cipher.ENCRYPT_MODE, new MockKey());
+ assertEquals(mockProvider, c.getProvider());
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+
public void testCipher_getInstance_SuppliedProviderNotRegistered_MultipartTransform_Success()
throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
@@ -1136,6 +1154,9 @@ public final class CipherTest extends TestCase {
final AlgorithmParameterSpec decryptSpec = getDecryptAlgorithmParameterSpec(encryptSpec, c);
int decryptMode = getDecryptMode(algorithm);
+
+ test_Cipher_init_Decrypt_NullParameters(c, decryptMode, encryptKey, decryptSpec != null);
+
c.init(decryptMode, encryptKey, decryptSpec);
assertEquals(cipherID + " getBlockSize() decryptMode",
getExpectedBlockSize(algorithm, decryptMode, providerName), c.getBlockSize());
@@ -1268,6 +1289,53 @@ public final class CipherTest extends TestCase {
}
}
+ private void test_Cipher_init_Decrypt_NullParameters(Cipher c, int decryptMode, Key encryptKey,
+ boolean needsParameters) throws Exception {
+ try {
+ c.init(decryptMode, encryptKey, (AlgorithmParameterSpec) null);
+ if (needsParameters) {
+ fail("Should throw InvalidAlgorithmParameterException with null parameters");
+ }
+ } catch (InvalidAlgorithmParameterException e) {
+ if (!needsParameters) {
+ throw e;
+ }
+ }
+
+ try {
+ c.init(decryptMode, encryptKey, (AlgorithmParameterSpec) null, (SecureRandom) null);
+ if (needsParameters) {
+ fail("Should throw InvalidAlgorithmParameterException with null parameters");
+ }
+ } catch (InvalidAlgorithmParameterException e) {
+ if (!needsParameters) {
+ throw e;
+ }
+ }
+
+ try {
+ c.init(decryptMode, encryptKey, (AlgorithmParameters) null);
+ if (needsParameters) {
+ fail("Should throw InvalidAlgorithmParameterException with null parameters");
+ }
+ } catch (InvalidAlgorithmParameterException e) {
+ if (!needsParameters) {
+ throw e;
+ }
+ }
+
+ try {
+ c.init(decryptMode, encryptKey, (AlgorithmParameters) null, (SecureRandom) null);
+ if (needsParameters) {
+ fail("Should throw InvalidAlgorithmParameterException with null parameters");
+ }
+ } catch (InvalidAlgorithmParameterException e) {
+ if (!needsParameters) {
+ throw e;
+ }
+ }
+ }
+
public void testInputPKCS1Padding() throws Exception {
for (String provider : RSA_PROVIDERS) {
testInputPKCS1Padding(provider);
@@ -2797,6 +2865,17 @@ public final class CipherTest extends TestCase {
}
}
+ public void testCipher_Update_WithZeroLengthInput_ReturnsNull() throws Exception {
+ SecretKey key = new SecretKeySpec(AES_128_KEY, "AES");
+ Cipher c = Cipher.getInstance("AES/ECB/NoPadding");
+ c.init(Cipher.ENCRYPT_MODE, key);
+ assertNull(c.update(new byte[0]));
+ assertNull(c.update(new byte[c.getBlockSize() * 2], 0, 0));
+
+ // Try with non-zero offset just in case the implementation mixes up offset and inputLen
+ assertNull(c.update(new byte[c.getBlockSize() * 2], 16, 0));
+ }
+
private void checkCipher_ShortBlock_Failure(CipherTestParam p, String provider) throws Exception {
SecretKey key = new SecretKeySpec(p.key, "AES");
Cipher c = Cipher.getInstance(
diff --git a/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java b/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java
new file mode 100644
index 0000000..ba03f75
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/crypto/KeyAgreementTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.javax.crypto;
+
+import java.security.Provider;
+import java.security.Security;
+
+import javax.crypto.KeyAgreement;
+
+import junit.framework.TestCase;
+
+public class KeyAgreementTest extends TestCase {
+ private static abstract class MockProvider extends Provider {
+ public MockProvider(String name) {
+ super(name, 1.0, "Mock provider used for testing");
+ setup();
+ }
+
+ public abstract void setup();
+ }
+
+ public void testKeyAgreement_getInstance_SuppliedProviderNotRegistered_Success()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+ }
+ };
+
+ {
+ KeyAgreement c = KeyAgreement.getInstance("FOO", mockProvider);
+ c.init(new MockKey());
+ assertEquals(mockProvider, c.getProvider());
+ }
+ }
+
+ public void testKeyAgreement_getInstance_DoesNotSupportKeyClass_Success()
+ throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("KeyAgreement.FOO", MockKeyAgreementSpi.AllKeyTypes.class.getName());
+ put("KeyAgreement.FOO SupportedKeyClasses", "none");
+
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ KeyAgreement c = KeyAgreement.getInstance("FOO", mockProvider);
+ c.init(new MockKey());
+ assertEquals(mockProvider, c.getProvider());
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/javax/crypto/KeyGeneratorTest.java b/luni/src/test/java/libcore/javax/crypto/KeyGeneratorTest.java
index 8bbd548..5763562 100644
--- a/luni/src/test/java/libcore/javax/crypto/KeyGeneratorTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/KeyGeneratorTest.java
@@ -44,6 +44,15 @@ public class KeyGeneratorTest extends TestCase {
if (!type.equals("KeyGenerator")) {
continue;
}
+
+ // Do not test AndroidKeyStore's KeyGenerator. It cannot be initialized without
+ // providing AndroidKeyStore-specific algorithm parameters.
+ // It's OKish not to test AndroidKeyStore's KeyGenerator here because it's tested
+ // by cts/tests/test/keystore.
+ if ("AndroidKeyStore".equals(provider.getName())) {
+ continue;
+ }
+
String algorithm = service.getAlgorithm();
try {
// KeyGenerator.getInstance(String)
diff --git a/luni/src/test/java/libcore/javax/crypto/MockKeyAgreementSpi.java b/luni/src/test/java/libcore/javax/crypto/MockKeyAgreementSpi.java
new file mode 100644
index 0000000..574dbeb
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/crypto/MockKeyAgreementSpi.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.javax.crypto;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.KeyAgreementSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+
+/**
+ * Mock KeyAgreementSpi used by {@link KeyAgreementTest}.
+ */
+public class MockKeyAgreementSpi extends KeyAgreementSpi {
+ public static class SpecificKeyTypes extends MockKeyAgreementSpi {
+ @Override
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ if (!(key instanceof MockKey)) {
+ throw new InvalidKeyException("Must be MockKey!");
+ }
+ }
+ }
+
+ public static class SpecificKeyTypes2 extends MockKeyAgreementSpi {
+ @Override
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ System.err.println("Checking key of type " + key.getClass().getName());
+ if (!(key instanceof MockKey2)) {
+ throw new InvalidKeyException("Must be MockKey2!");
+ }
+ }
+ }
+
+ public static class AllKeyTypes extends MockKeyAgreementSpi {
+ }
+
+ public void checkKeyType(Key key) throws InvalidKeyException {
+ }
+
+ @Override
+ protected Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException,
+ IllegalStateException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected byte[] engineGenerateSecret() throws IllegalStateException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected int engineGenerateSecret(byte[] sharedSecret, int offset)
+ throws IllegalStateException, ShortBufferException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected SecretKey engineGenerateSecret(String algorithm) throws IllegalStateException,
+ NoSuchAlgorithmException, InvalidKeyException {
+ throw new UnsupportedOperationException("not implemented");
+ }
+
+ @Override
+ protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException {
+ checkKeyType(key);
+ }
+
+ @Override
+ protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ checkKeyType(key);
+ }
+}
diff --git a/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java b/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
index feecebe..07ecd12 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/DefaultHostnameVerifierTest.java
@@ -22,6 +22,7 @@ import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.security.PublicKey;
+import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@@ -30,21 +31,32 @@ import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
-import javax.net.ssl.DefaultHostnameVerifier;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
import javax.security.auth.x500.X500Principal;
import junit.framework.TestCase;
+/**
+ * Tests for the platform-default {@link HostnameVerifier} as provided by
+ * {@link HttpsURLConnection#getDefaultHostnameVerifier()}.
+ */
public final class DefaultHostnameVerifierTest extends TestCase {
private static final int ALT_UNKNOWN = 0;
private static final int ALT_DNS_NAME = 2;
private static final int ALT_IPA_NAME = 7;
- private final DefaultHostnameVerifier verifier = new DefaultHostnameVerifier();
+ private final HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
public void testVerify() {
- assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("cn=imap.g.com")));
- assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("cn=imap2.g.com")));
- assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("cn=sub.imap.g.com")));
+ assertTrue(verifyWithServerCertificate(
+ "imap.g.com", new StubX509Certificate("cn=imap.g.com")));
+ assertFalse(verifyWithServerCertificate(
+ "imap.g.com", new StubX509Certificate("cn=imap2.g.com")));
+ assertFalse(verifyWithServerCertificate(
+ "imap.g.com", new StubX509Certificate("cn=sub.imap.g.com")));
}
/**
@@ -52,32 +64,33 @@ public final class DefaultHostnameVerifierTest extends TestCase {
* be used as the identity and the CN should be ignored.
*/
public void testSubjectAltNameAndCn() {
- assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("")
- .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
- assertFalse(verifier.verify("imap.g.com", new StubX509Certificate("cn=imap.g.com")
+ assertFalse(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
- assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("")
+ assertFalse(
+ verifyWithServerCertificate("imap.g.com", new StubX509Certificate("cn=imap.g.com")
+ .addSubjectAlternativeName(ALT_DNS_NAME, "a.y.com")));
+ assertTrue(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")));
}
public void testSubjectAltNameWithWildcard() {
- assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("")
+ assertTrue(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_DNS_NAME, "*.g.com")));
}
public void testSubjectAltNameWithIpAddress() {
- assertTrue(verifier.verify("1.2.3.4", new StubX509Certificate("")
+ assertTrue(verifyWithServerCertificate("1.2.3.4", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")));
- assertFalse(verifier.verify("1.2.3.5", new StubX509Certificate("")
+ assertFalse(verifyWithServerCertificate("1.2.3.5", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")));
- assertTrue(verifier.verify("192.168.100.1", new StubX509Certificate("")
+ assertTrue(verifyWithServerCertificate("192.168.100.1", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")
.addSubjectAlternativeName(ALT_IPA_NAME, "192.168.100.1")));
}
public void testUnknownSubjectAltName() {
// Has unknown subject alternative names
- assertTrue(verifier.verify("imap.g.com", new StubX509Certificate("")
+ assertTrue(verifyWithServerCertificate("imap.g.com", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
.addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -85,7 +98,7 @@ public final class DefaultHostnameVerifierTest extends TestCase {
.addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
.addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
- assertTrue(verifier.verify("2.33.44.55", new StubX509Certificate("")
+ assertTrue(verifyWithServerCertificate("2.33.44.55", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
.addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -93,7 +106,7 @@ public final class DefaultHostnameVerifierTest extends TestCase {
.addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
.addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
- assertFalse(verifier.verify("g.com", new StubX509Certificate("")
+ assertFalse(verifyWithServerCertificate("g.com", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
.addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -101,7 +114,7 @@ public final class DefaultHostnameVerifierTest extends TestCase {
.addSubjectAlternativeName(ALT_DNS_NAME, "imap.g.com")
.addSubjectAlternativeName(ALT_IPA_NAME, "2.33.44.55")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
- assertFalse(verifier.verify("2.33.44.1", new StubX509Certificate("")
+ assertFalse(verifyWithServerCertificate("2.33.44.1", new StubX509Certificate("")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 1")
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 2")
.addSubjectAlternativeName(ALT_DNS_NAME, "a.b.c.d")
@@ -111,40 +124,129 @@ public final class DefaultHostnameVerifierTest extends TestCase {
.addSubjectAlternativeName(ALT_UNKNOWN, "random string 3")));
}
- public void testWildcardMatchesWildcardSuffix() {
- assertTrue(verifier.verifyHostName("b.c.d", "*.b.c.d"));
- assertTrue(verifier.verifyHostName("imap.google.com", "*.imap.google.com"));
- assertFalse(verifier.verifyHostName("imap.google.com.au", "*.imap.google.com"));
+ public void testWildcardsRejectedForIpAddress() {
+ assertFalse(verifyWithServerCertificate("1.2.3.4", new StubX509Certificate("cn=*.2.3.4")));
+ assertFalse(verifyWithServerCertificate("1.2.3.4", new StubX509Certificate("cn=*.2.3.4")
+ .addSubjectAlternativeName(ALT_IPA_NAME, "*.2.3.4")
+ .addSubjectAlternativeName(ALT_DNS_NAME, "*.2.3.4")));
+ assertFalse(verifyWithServerCertificate(
+ "2001:1234::1", new StubX509Certificate("cn=*:1234::1")));
+ assertFalse(verifyWithServerCertificate(
+ "2001:1234::1", new StubX509Certificate("cn=*:1234::1")
+ .addSubjectAlternativeName(ALT_IPA_NAME, "*:1234::1")
+ .addSubjectAlternativeName(ALT_DNS_NAME, "*:1234::1")));
+ }
+
+ public void testNullParameters() {
+ // Confirm that neither of the parameters used later in the test cause the verifier to blow
+ // up
+ String hostname = "www.example.com";
+ StubSSLSession session = new StubSSLSession();
+ session.peerCertificates =
+ new Certificate[] {new StubX509Certificate("cn=www.example.com")};
+ verifier.verify(hostname, session);
+
+ try {
+ verifier.verify(hostname, null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ verifier.verify(null, session);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void testInvalidDomainNames() {
+ assertFalse(verifyWithDomainNamePattern("", ""));
+ assertFalse(verifyWithDomainNamePattern(".test.example.com", ".test.example.com"));
+ assertFalse(verifyWithDomainNamePattern("ex*ample.com", "ex*ample.com"));
+ assertFalse(verifyWithDomainNamePattern("example.com..", "example.com."));
+ assertFalse(verifyWithDomainNamePattern("example.com.", "example.com.."));
+ }
+
+ public void testWildcardCharacterMustBeLeftMostLabelOnly() {
+ assertFalse(verifyWithDomainNamePattern("test.www.example.com", "test.*.example.com"));
+ assertFalse(verifyWithDomainNamePattern("www.example.com", "www.*.com"));
+ assertFalse(verifyWithDomainNamePattern("www.example.com", "www.example.*"));
+ assertFalse(verifyWithDomainNamePattern("www.example.com", "*www.example.com"));
+ assertFalse(verifyWithDomainNamePattern("www.example.com", "*w.example.com"));
+ assertFalse(verifyWithDomainNamePattern("www.example.com", "w*w.example.com"));
+ assertFalse(verifyWithDomainNamePattern("www.example.com", "w*.example.com"));
+ assertFalse(verifyWithDomainNamePattern("www.example.com", "www*.example.com"));
}
- public void testWildcardMatchingSubstring() {
- assertTrue(verifier.verifyHostName("b.c.d", "b*.c.d"));
- assertTrue(verifier.verifyHostName("imap.google.com", "ima*.google.com"));
- assertFalse(verifier.verifyHostName("imap.google.com.au", "ima*.google.com"));
+ public void testWildcardCannotMatchEmptyLabel() {
+ assertFalse(verifyWithDomainNamePattern("example.com", "*.example.com"));
+ assertFalse(verifyWithDomainNamePattern(".example.com", "*.example.com"));
}
- public void testWildcardMatchingEmptySubstring() {
- assertTrue(verifier.verifyHostName("imap.google.com", "imap*.google.com"));
- assertFalse(verifier.verifyHostName("imap.google.com.au", "imap*.google.com"));
+ public void testWildcardCannotMatchChildDomain() {
+ assertFalse(verifyWithDomainNamePattern("sub.www.example.com", "*.example.com"));
}
- public void testWildcardMatchesChildDomain() {
- assertFalse(verifier.verifyHostName("a.b.c.d", "*.c.d"));
+ public void testWildcardRejectedForSingleLabelPatterns() {
+ assertFalse(verifyWithDomainNamePattern("d", "*"));
+ assertFalse(verifyWithDomainNamePattern("d.", "*."));
+ assertFalse(verifyWithDomainNamePattern("d", "d*"));
+ assertFalse(verifyWithDomainNamePattern("d.", "d*."));
+ assertFalse(verifyWithDomainNamePattern("d", "*d"));
+ assertFalse(verifyWithDomainNamePattern("d.", "*d."));
+ assertFalse(verifyWithDomainNamePattern("ddd", "d*d"));
+ assertFalse(verifyWithDomainNamePattern("ddd.", "d*d."));
+ }
+
+ public void testNoPrefixMatch() {
+ assertFalse(verifyWithDomainNamePattern("imap.google.com.au", "imap.google.com"));
+ assertFalse(verifyWithDomainNamePattern("imap.google.com.au", "*.google.com"));
}
public void testVerifyHostName() {
- assertTrue(verifier.verifyHostName("a.b.c.d", "a.b.c.d"));
- assertTrue(verifier.verifyHostName("a.b.c.d", "*.b.c.d"));
- assertFalse(verifier.verifyHostName("a.b.c.d", "*.*.c.d"));
- assertTrue(verifier.verifyHostName("imap.google.com", "imap.google.com"));
- assertFalse(verifier.verifyHostName("imap2.google.com", "imap.google.com"));
- assertTrue(verifier.verifyHostName("imap.google.com", "*.google.com"));
- assertTrue(verifier.verifyHostName("imap2.google.com", "*.google.com"));
- assertFalse(verifier.verifyHostName("imap.google.com", "*.googl.com"));
- assertFalse(verifier.verifyHostName("imap2.google2.com", "*.google3.com"));
- assertFalse(verifier.verifyHostName("imap.google.com", "a*.google.com"));
- assertFalse(verifier.verifyHostName("imap.google.com", "ix*.google.com"));
- assertTrue(verifier.verifyHostName("imap.google.com", "iMap.Google.Com"));
+ assertTrue(verifyWithDomainNamePattern("a.b.c.d", "a.b.c.d"));
+ assertTrue(verifyWithDomainNamePattern("a.b.c.d", "*.b.c.d"));
+ assertFalse(verifyWithDomainNamePattern("a.b.c.d", "*.*.c.d"));
+ assertTrue(verifyWithDomainNamePattern("imap.google.com", "imap.google.com"));
+ assertFalse(verifyWithDomainNamePattern("imap2.google.com", "imap.google.com"));
+ assertTrue(verifyWithDomainNamePattern("imap.google.com", "*.google.com"));
+ assertTrue(verifyWithDomainNamePattern("imap2.google.com", "*.google.com"));
+ assertFalse(verifyWithDomainNamePattern("imap.google.com", "*.googl.com"));
+ assertFalse(verifyWithDomainNamePattern("imap2.google2.com", "*.google3.com"));
+ assertFalse(verifyWithDomainNamePattern("imap.google.com", "a*.google.com"));
+ assertFalse(verifyWithDomainNamePattern("imap.google.com", "ix*.google.com"));
+ assertTrue(verifyWithDomainNamePattern("imap.google.com", "iMap.Google.Com"));
+ assertTrue(verifyWithDomainNamePattern("weird", "weird"));
+ assertTrue(verifyWithDomainNamePattern("weird", "weird."));
+
+ // Wildcards rejected for domain names consisting of fewer than two labels (excluding root).
+ assertFalse(verifyWithDomainNamePattern("weird", "weird*"));
+ assertFalse(verifyWithDomainNamePattern("weird", "*weird"));
+ assertFalse(verifyWithDomainNamePattern("weird", "weird*."));
+ assertFalse(verifyWithDomainNamePattern("weird", "weird.*"));
+ }
+
+ public void testVerifyAbsoluteHostName() {
+ assertTrue(verifyWithDomainNamePattern("a.b.c.d.", "a.b.c.d"));
+ assertTrue(verifyWithDomainNamePattern("a.b.c.d.", "*.b.c.d"));
+ assertFalse(verifyWithDomainNamePattern("a.b.c.d.", "*.*.c.d"));
+ assertTrue(verifyWithDomainNamePattern("imap.google.com.", "imap.google.com"));
+ assertFalse(verifyWithDomainNamePattern("imap2.google.com.", "imap.google.com"));
+ assertTrue(verifyWithDomainNamePattern("imap.google.com.", "*.google.com"));
+ assertTrue(verifyWithDomainNamePattern("imap2.google.com.", "*.google.com"));
+ assertFalse(verifyWithDomainNamePattern("imap.google.com.", "*.googl.com"));
+ assertFalse(verifyWithDomainNamePattern("imap2.google2.com.", "*.google3.com"));
+ assertFalse(verifyWithDomainNamePattern("imap.google.com.", "a*.google.com"));
+ assertFalse(verifyWithDomainNamePattern("imap.google.com.", "ix*.google.com"));
+ assertTrue(verifyWithDomainNamePattern("imap.google.com.", "iMap.Google.Com"));
+ assertTrue(verifyWithDomainNamePattern("weird.", "weird"));
+ assertTrue(verifyWithDomainNamePattern("weird.", "weird."));
+
+ // Wildcards rejected for domain names consisting of fewer than two labels (excluding root).
+ assertFalse(verifyWithDomainNamePattern("weird.", "*weird"));
+ assertFalse(verifyWithDomainNamePattern("weird.", "weird*"));
+ assertFalse(verifyWithDomainNamePattern("weird.", "weird*."));
+ assertFalse(verifyWithDomainNamePattern("weird.", "weird.*"));
}
public void testSubjectOnlyCert() throws Exception {
@@ -168,8 +270,8 @@ public final class DefaultHostnameVerifierTest extends TestCase {
+ "rs2oQLwOLnuifH52ey9+tJguabo+brlYYigAuWWFEzJfBzikDkIwnE/L7wlrypIk\n"
+ "taXDWI4=\n"
+ "-----END CERTIFICATE-----");
- assertTrue(verifier.verify("www.example.com", cert));
- assertFalse(verifier.verify("www2.example.com", cert));
+ assertTrue(verifyWithServerCertificate("www.example.com", cert));
+ assertFalse(verifyWithServerCertificate("www2.example.com", cert));
}
public void testSubjectAltOnlyCert() throws Exception {
@@ -192,8 +294,8 @@ public final class DefaultHostnameVerifierTest extends TestCase {
+ "JPRynf9244Pn0Sr/wsnmdsTRFIFYynrc51hQ7DkwbUxpcaewkZzilru/SwZ3+pPT\n"
+ "9JSqm5hJ1pg5WDlPkW7c/1VA0/141N52Q8MIU+2ZpuOj\n"
+ "-----END CERTIFICATE-----");
- assertTrue(verifier.verify("www.example.com", cert));
- assertFalse(verifier.verify("www2.example.com", cert));
+ assertTrue(verifyWithServerCertificate("www.example.com", cert));
+ assertFalse(verifyWithServerCertificate("www2.example.com", cert));
}
public void testSubjectWithAltNamesCert() throws Exception {
@@ -219,10 +321,10 @@ public final class DefaultHostnameVerifierTest extends TestCase {
+ "hrTVypLSoRXuTB2aWilu4p6aNh84xTdyqo2avtNr2MiQMZIcdamBq8LdBIAShFXI\n"
+ "h5G2eVGXH/Y=\n"
+ "-----END CERTIFICATE-----");
- assertFalse(verifier.verify("www.example.com", cert));
- assertTrue(verifier.verify("www2.example.com", cert));
- assertTrue(verifier.verify("www3.example.com", cert));
- assertFalse(verifier.verify("www4.example.com", cert));
+ assertFalse(verifyWithServerCertificate("www.example.com", cert));
+ assertTrue(verifyWithServerCertificate("www2.example.com", cert));
+ assertTrue(verifyWithServerCertificate("www3.example.com", cert));
+ assertFalse(verifyWithServerCertificate("www4.example.com", cert));
}
public void testSubjectWithWildAltNamesCert() throws Exception {
@@ -247,11 +349,11 @@ public final class DefaultHostnameVerifierTest extends TestCase {
+ "Y3R0HZvKzNIU3pwAm69HCJoG+/9MZEIDJb0WJc5UygxDT45XE9zQMQe4dBOTaNXT\n"
+ "+ntgaB62kE10HzrzpqXAgoAWxWK4RzFcUpBWw9qYq9xOCewJ\n"
+ "-----END CERTIFICATE-----");
- assertFalse(verifier.verify("www.example.com", cert));
- assertFalse(verifier.verify("www2.example.com", cert));
- assertTrue(verifier.verify("www.example2.com", cert));
- assertTrue(verifier.verify("abc.example2.com", cert));
- assertFalse(verifier.verify("www.example3.com", cert));
+ assertFalse(verifyWithServerCertificate("www.example.com", cert));
+ assertFalse(verifyWithServerCertificate("www2.example.com", cert));
+ assertTrue(verifyWithServerCertificate("www.example2.com", cert));
+ assertTrue(verifyWithServerCertificate("abc.example2.com", cert));
+ assertFalse(verifyWithServerCertificate("www.example3.com", cert));
}
public void testWildAltNameOnlyCert() throws Exception {
@@ -274,9 +376,9 @@ public final class DefaultHostnameVerifierTest extends TestCase {
+ "va++ow5r1VxQXFJc0ZPzsDo+6TlktoDHaRQJGMqQomqHWT4i7F5UZgf6BHGfEUPU\n"
+ "qep+GsF3QRHSBtpObWkVDZNFvky3a1iZ2q25+hFIqQ==\n"
+ "-----END CERTIFICATE-----");
- assertTrue(verifier.verify("www.example.com", cert));
- assertTrue(verifier.verify("www2.example.com", cert));
- assertFalse(verifier.verify("www.example2.com", cert));
+ assertTrue(verifyWithServerCertificate("www.example.com", cert));
+ assertTrue(verifyWithServerCertificate("www2.example.com", cert));
+ assertFalse(verifyWithServerCertificate("www.example2.com", cert));
}
public void testAltIpOnlyCert() throws Exception {
@@ -299,8 +401,48 @@ public final class DefaultHostnameVerifierTest extends TestCase {
+ "WPjHQcWfpkFzAF5wyOq0kveVfx0g5xPhOVDd+U+q7WastbXICpCoHp9FxISmZVik\n"
+ "sAyifp8agkYdzaSh55fFmKXlFnRsQw==\n"
+ "-----END CERTIFICATE-----");
- assertTrue(verifier.verify("192.168.10.1", cert));
- assertFalse(verifier.verify("192.168.10.2", cert));
+ assertTrue(verifyWithServerCertificate("192.168.10.1", cert));
+ assertFalse(verifyWithServerCertificate("192.168.10.2", cert));
+ }
+
+ /**
+ * Verifies the provided hostname against the provided domain name pattern from server
+ * certificate.
+ */
+ private boolean verifyWithDomainNamePattern(String hostname, String pattern) {
+ StubSSLSession session = new StubSSLSession();
+
+ // Verify using a certificate where the pattern is in the CN
+ session.peerCertificates = new Certificate[] {
+ new StubX509Certificate("cn=\"" + pattern + "\"")
+ };
+ boolean resultWhenPatternInCn = verifier.verify(hostname, session);
+
+ // Verify using a certificate where the pattern is in a DNS SubjectAltName
+ session.peerCertificates = new Certificate[] {
+ new StubX509Certificate("ou=test")
+ .addSubjectAlternativeName(ALT_DNS_NAME, pattern)
+ };
+ boolean resultWhenPatternInSubjectAltName = verifier.verify(hostname, session);
+
+ // Assert that in both cases the verifier gives the same result
+ if (resultWhenPatternInCn != resultWhenPatternInSubjectAltName) {
+ fail("Different results between pattern in CN and SubjectAltName."
+ + " hostname : " + hostname + ", pattern: " + pattern
+ + ", when pattern in CN: " + resultWhenPatternInCn
+ + ", when pattern in SubjectAltName: " + resultWhenPatternInSubjectAltName);
+ }
+ return resultWhenPatternInCn;
+ }
+
+ /**
+ * Verifies the provided hostname against the provided server certificate.
+ */
+ private boolean verifyWithServerCertificate(String hostname, X509Certificate certificate) {
+ StubSSLSession session = new StubSSLSession();
+ session.peerCertificates =
+ (certificate != null) ? new Certificate[] {certificate} : new Certificate[0];
+ return verifier.verify(hostname, session);
}
X509Certificate parseCertificate(String encoded) throws Exception {
@@ -308,6 +450,117 @@ public final class DefaultHostnameVerifierTest extends TestCase {
return (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(in);
}
+ private static class StubSSLSession implements SSLSession {
+
+ public Certificate[] peerCertificates = new Certificate[0];
+
+ @Override
+ public int getApplicationBufferSize() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getCipherSuite() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getCreationTime() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public byte[] getId() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getLastAccessedTime() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Certificate[] getLocalCertificates() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Principal getLocalPrincipal() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getPacketBufferSize() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+ throws SSLPeerUnverifiedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+ return peerCertificates;
+ }
+
+ @Override
+ public String getPeerHost() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getPeerPort() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getProtocol() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SSLSessionContext getSessionContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object getValue(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] getValueNames() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void invalidate() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public void putValue(String name, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeValue(String name) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
private static class StubX509Certificate extends X509Certificate {
private final X500Principal subjectX500Principal;
private Collection<List<?>> subjectAlternativeNames;
diff --git a/luni/src/test/java/libcore/javax/net/ssl/HttpsURLConnectionTest.java b/luni/src/test/java/libcore/javax/net/ssl/HttpsURLConnectionTest.java
new file mode 100644
index 0000000..cbaea20
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/net/ssl/HttpsURLConnectionTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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 libcore.javax.net.ssl;
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.net.URL;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+
+public class HttpsURLConnectionTest extends TestCase {
+
+ /**
+ * HTTPS URL which cannot be resolved and is thus safe to use in tests where network traffic
+ * should be avoided.
+ */
+ private static final String UNRESOLVABLE_HTTPS_URL = "https:///";
+
+ public void testDefaultHostnameVerifierNotNull() {
+ assertNotNull(HttpsURLConnection.getDefaultHostnameVerifier());
+ }
+
+ public void testDefaultHostnameVerifierUsedForNewConnectionsByDefault() throws IOException {
+ HostnameVerifier originalHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
+ HttpsURLConnection connection =
+ (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+ try {
+ assertSame(originalHostnameVerifier, connection.getHostnameVerifier());
+ } finally {
+ connection.disconnect();
+ }
+
+ HostnameVerifier anotherVerifier = new FakeHostnameVerifier();
+ try {
+ HttpsURLConnection.setDefaultHostnameVerifier(anotherVerifier);
+ connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+ try {
+ assertSame(anotherVerifier, connection.getHostnameVerifier());
+ } finally {
+ connection.disconnect();
+ }
+
+ HttpsURLConnection.setDefaultHostnameVerifier(originalHostnameVerifier);
+ connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+ try {
+ assertSame(originalHostnameVerifier, connection.getHostnameVerifier());
+ } finally {
+ connection.disconnect();
+ }
+ } finally {
+ HttpsURLConnection.setDefaultHostnameVerifier(originalHostnameVerifier);
+ }
+ }
+
+ public void testDefaultSSLSocketFactoryNotNull() {
+ assertNotNull(HttpsURLConnection.getDefaultSSLSocketFactory());
+ }
+
+ public void testDefaultSSLSocketFactoryUsedForNewConnectionsByDefault() throws IOException {
+ SSLSocketFactory originalFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
+ HttpsURLConnection connection =
+ (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+ try {
+ assertSame(originalFactory, connection.getSSLSocketFactory());
+ } finally {
+ connection.disconnect();
+ }
+
+ SSLSocketFactory anotherFactory = new SSLSocketFactoryTest.FakeSSLSocketFactory();
+ try {
+ HttpsURLConnection.setDefaultSSLSocketFactory(anotherFactory);
+ connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+ try {
+ assertSame(anotherFactory, connection.getSSLSocketFactory());
+ } finally {
+ connection.disconnect();
+ }
+
+ HttpsURLConnection.setDefaultSSLSocketFactory(originalFactory);
+ connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+ try {
+ assertSame(originalFactory, connection.getSSLSocketFactory());
+ } finally {
+ connection.disconnect();
+ }
+ } finally {
+ HttpsURLConnection.setDefaultSSLSocketFactory(originalFactory);
+ }
+ }
+
+ private static class FakeHostnameVerifier implements HostnameVerifier {
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ }
+}
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 dccadbd..533849c 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLContextTest.java
@@ -26,7 +26,6 @@ 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;
@@ -82,14 +81,14 @@ public class SSLContextTest extends TestCase {
}
public void test_SSLContext_defaultConfiguration() throws Exception {
- SSLDefaultConfigurationAsserts.assertSSLContext(SSLContext.getDefault());
+ SSLConfigurationAsserts.assertSSLContextDefaultConfiguration(SSLContext.getDefault());
for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
SSLContext sslContext = SSLContext.getInstance(protocol);
if (!protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
sslContext.init(null, null, null);
}
- SSLDefaultConfigurationAsserts.assertSSLContext(sslContext);
+ SSLConfigurationAsserts.assertSSLContextDefaultConfiguration(sslContext);
}
}
@@ -149,6 +148,27 @@ public class SSLContextTest extends TestCase {
sslContext);
}
+ public void test_SSLContext_init_correctProtocolVersionsEnabled() throws Exception {
+ for (String tlsVersion : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+ // Don't test the "Default" instance.
+ if (StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT.equals(tlsVersion)) {
+ continue;
+ }
+
+ SSLContext context = SSLContext.getInstance(tlsVersion);
+ context.init(null, null, null);
+
+ StandardNames.assertSSLContextEnabledProtocols(tlsVersion, ((SSLSocket) (context.getSocketFactory()
+ .createSocket())).getEnabledProtocols());
+ StandardNames.assertSSLContextEnabledProtocols(tlsVersion, ((SSLServerSocket) (context
+ .getServerSocketFactory().createServerSocket())).getEnabledProtocols());
+ StandardNames.assertSSLContextEnabledProtocols(tlsVersion, context.getDefaultSSLParameters()
+ .getProtocols());
+ StandardNames.assertSSLContextEnabledProtocols(tlsVersion, context.createSSLEngine()
+ .getEnabledProtocols());
+ }
+ }
+
private static void assertEnabledCipherSuites(
List<String> expectedCipherSuites, SSLContext sslContext) throws Exception {
assertContentsInOrder(
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
index df4585f..5e3a3d5 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
@@ -65,7 +65,7 @@ public class SSLEngineTest extends TestCase {
}
public void test_SSLEngine_defaultConfiguration() throws Exception {
- SSLDefaultConfigurationAsserts.assertSSLEngine(
+ SSLConfigurationAsserts.assertSSLEngineDefaultConfiguration(
TestSSLContext.create().clientContext.createSSLEngine());
}
@@ -203,24 +203,31 @@ public class SSLEngineTest extends TestCase {
: new String[] { cipherSuite });
// Check that handshake succeeds.
- TestSSLEnginePair pair = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
- @Override
- void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
- client.setEnabledCipherSuites(cipherSuiteArray);
- server.setEnabledCipherSuites(cipherSuiteArray);
+ TestSSLEnginePair pair = null;
+ try {
+ pair = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
+ @Override
+ void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+ client.setEnabledCipherSuites(cipherSuiteArray);
+ server.setEnabledCipherSuites(cipherSuiteArray);
+ }
+ });
+ assertConnected(pair);
+
+ boolean needsRecordSplit =
+ ("TLS".equalsIgnoreCase(c.clientContext.getProtocol())
+ || "SSLv3".equalsIgnoreCase(c.clientContext.getProtocol()))
+ && cipherSuite.contains("_CBC_");
+
+ assertSendsCorrectly("This is the client. Hello!".getBytes(),
+ pair.client, pair.server, needsRecordSplit);
+ assertSendsCorrectly("This is the server. Hi!".getBytes(),
+ pair.server, pair.client, needsRecordSplit);
+ } finally {
+ if (pair != null) {
+ pair.close();
}
- });
- assertConnected(pair);
-
- boolean needsRecordSplit =
- ("TLS".equalsIgnoreCase(c.clientContext.getProtocol())
- || "SSLv3".equalsIgnoreCase(c.clientContext.getProtocol()))
- && cipherSuite.contains("_CBC_");
-
- assertSendsCorrectly("This is the client. Hello!".getBytes(),
- pair.client, pair.server, needsRecordSplit);
- assertSendsCorrectly("This is the server. Hi!".getBytes(),
- pair.server, pair.client, needsRecordSplit);
+ }
// Check that handshake fails when the server does not possess the private key
// corresponding to the server's certificate. This is achieved by using SSLContext
@@ -234,17 +241,23 @@ public class SSLEngineTest extends TestCase {
serverAuthenticatedUsingPublicKey = false;
}
if (serverAuthenticatedUsingPublicKey) {
+ TestSSLEnginePair p = null;
try {
- TestSSLEnginePair p = TestSSLEnginePair.create(
+ p = TestSSLEnginePair.create(
cWithWrongPrivateKeys, new TestSSLEnginePair.Hooks() {
- @Override
+ @Override
void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
- client.setEnabledCipherSuites(cipherSuiteArray);
- server.setEnabledCipherSuites(cipherSuiteArray);
- }
- });
+ client.setEnabledCipherSuites(cipherSuiteArray);
+ server.setEnabledCipherSuites(cipherSuiteArray);
+ }
+ });
assertNotConnected(p);
- } catch (IOException expected) {}
+ } catch (IOException expected) {
+ } finally {
+ if (p != null) {
+ p.close();
+ }
+ }
}
} catch (Exception e) {
String message = ("Problem trying to connect cipher suite " + cipherSuite);
@@ -432,21 +445,28 @@ public class SSLEngineTest extends TestCase {
fail();
} catch (IllegalStateException expected) {
}
+ c.close();
- assertConnected(TestSSLEnginePair.create(null));
+ TestSSLEnginePair p = TestSSLEnginePair.create(null);
+ assertConnected(p);
+ p.close();
- c.close();
}
public void test_SSLEngine_beginHandshake_noKeyStore() throws Exception {
TestSSLContext c = TestSSLContext.create(null, null, null, null, null, null, null, null,
SSLContext.getDefault(), SSLContext.getDefault());
+ SSLEngine[] p = null;
try {
// TODO Fix KnownFailure AlertException "NO SERVER CERTIFICATE FOUND"
// ServerHandshakeImpl.selectSuite should not select a suite without a required cert
- TestSSLEnginePair.connect(c, null);
+ p = TestSSLEnginePair.connect(c, null);
fail();
} catch (SSLHandshakeException expected) {
+ } finally {
+ if (p != null) {
+ TestSSLEnginePair.close(p);
+ }
}
c.close();
}
@@ -456,6 +476,7 @@ public class SSLEngineTest extends TestCase {
SSLEngine[] engines = TestSSLEnginePair.connect(c, null);
assertConnected(engines[0], engines[1]);
c.close();
+ TestSSLEnginePair.close(engines);
}
public void test_SSLEngine_getUseClientMode() throws Exception {
@@ -467,33 +488,47 @@ public class SSLEngineTest extends TestCase {
public void test_SSLEngine_setUseClientMode() throws Exception {
boolean[] finished;
+ TestSSLEnginePair p = null;
// client is client, server is server
finished = new boolean[2];
- assertConnected(test_SSLEngine_setUseClientMode(true, false, finished));
+ p = test_SSLEngine_setUseClientMode(true, false, finished);
+ assertConnected(p);
assertTrue(finished[0]);
assertTrue(finished[1]);
+ p.close();
// client is server, server is client
finished = new boolean[2];
- assertConnected(test_SSLEngine_setUseClientMode(false, true, finished));
+ p = test_SSLEngine_setUseClientMode(false, true, finished);
+ assertConnected(p);
assertTrue(finished[0]);
assertTrue(finished[1]);
+ p.close();
// both are client
/*
* Our implementation throws an SSLHandshakeException, but RI just
* stalls forever
*/
+ p = null;
try {
- assertNotConnected(test_SSLEngine_setUseClientMode(true, true, null));
+ p = test_SSLEngine_setUseClientMode(true, true, null);
+ assertNotConnected(p);
assertTrue(StandardNames.IS_RI);
} catch (SSLHandshakeException maybeExpected) {
assertFalse(StandardNames.IS_RI);
+ } finally {
+ if (p != null) {
+ p.close();
+ }
+
}
+ p = test_SSLEngine_setUseClientMode(false, false, null);
// both are server
- assertNotConnected(test_SSLEngine_setUseClientMode(false, false, null));
+ assertNotConnected(p);
+ p.close();
}
public void test_SSLEngine_setUseClientMode_afterHandshake() throws Exception {
@@ -510,6 +545,7 @@ public class SSLEngineTest extends TestCase {
fail();
} catch (IllegalArgumentException expected) {
}
+ pair.close();
}
private TestSSLEnginePair test_SSLEngine_setUseClientMode(final boolean clientClientMode,
@@ -572,6 +608,7 @@ public class SSLEngineTest extends TestCase {
p.client.getSession().getLocalCertificates());
clientAuthContext.close();
c.close();
+ p.close();
}
/**
@@ -591,6 +628,7 @@ public class SSLEngineTest extends TestCase {
});
assertConnected(p);
clientAuthContext.close();
+ p.close();
}
/**
@@ -604,8 +642,9 @@ public class SSLEngineTest extends TestCase {
TestSSLContext clientAuthContext
= TestSSLContext.create(TestKeyStore.getClient(),
TestKeyStore.getServer());
+ TestSSLEnginePair p = null;
try {
- TestSSLEnginePair.create(clientAuthContext,
+ p = TestSSLEnginePair.create(clientAuthContext,
new TestSSLEnginePair.Hooks() {
@Override
void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
@@ -616,6 +655,9 @@ public class SSLEngineTest extends TestCase {
} catch (SSLHandshakeException expected) {
} finally {
clientAuthContext.close();
+ if (p != null) {
+ p.close();
+ }
}
}
@@ -624,11 +666,13 @@ public class SSLEngineTest extends TestCase {
SSLEngine e = c.clientContext.createSSLEngine();
assertTrue(e.getEnableSessionCreation());
c.close();
+ TestSSLEnginePair.close(new SSLEngine[] { e });
}
public void test_SSLEngine_setEnableSessionCreation_server() throws Exception {
+ TestSSLEnginePair p = null;
try {
- TestSSLEnginePair p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
+ p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
@Override
void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
server.setEnableSessionCreation(false);
@@ -639,12 +683,17 @@ public class SSLEngineTest extends TestCase {
assertNotConnected(p);
} catch (SSLException maybeExpected) {
assertFalse(StandardNames.IS_RI);
+ } finally {
+ if (p != null) {
+ p.close();
+ }
}
}
public void test_SSLEngine_setEnableSessionCreation_client() throws Exception {
+ TestSSLEnginePair p = null;
try {
- TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
+ p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
@Override
void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
client.setEnableSessionCreation(false);
@@ -652,6 +701,10 @@ public class SSLEngineTest extends TestCase {
});
fail();
} catch (SSLException expected) {
+ } finally {
+ if (p != null) {
+ p.close();
+ }
}
}
@@ -735,5 +788,6 @@ public class SSLEngineTest extends TestCase {
assertNotNull(test.server);
assertNotNull(test.client);
assertConnected(test);
+ test.close();
}
}
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketFactoryTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketFactoryTest.java
index ea9c3f0..cda1fb8 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketFactoryTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketFactoryTest.java
@@ -22,7 +22,7 @@ import junit.framework.TestCase;
public class SSLServerSocketFactoryTest extends TestCase {
public void testDefaultConfiguration() throws Exception {
- SSLDefaultConfigurationAsserts.assertSSLServerSocketFactory(
+ SSLConfigurationAsserts.assertSSLServerSocketFactoryDefaultConfiguration(
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault());
}
}
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java
index 59c44c1..d2c0f48 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLServerSocketTest.java
@@ -24,7 +24,7 @@ import java.util.Arrays;
public class SSLServerSocketTest extends TestCase {
public void testDefaultConfiguration() throws Exception {
- SSLDefaultConfigurationAsserts.assertSSLServerSocket(
+ SSLConfigurationAsserts.assertSSLServerSocketDefaultConfiguration(
(SSLServerSocket) SSLServerSocketFactory.getDefault().createServerSocket());
}
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 acf69c0..83b690b 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketFactoryTest.java
@@ -210,7 +210,7 @@ public class SSLSocketFactoryTest extends TestCase {
}
public void test_SSLSocketFactory_defaultConfiguration() throws Exception {
- SSLDefaultConfigurationAsserts.assertSSLSocketFactory(
+ SSLConfigurationAsserts.assertSSLSocketFactoryDefaultConfiguration(
(SSLSocketFactory) SSLSocketFactory.getDefault());
}
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
index 4af7f5a..8e4519d 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
@@ -16,6 +16,8 @@
package libcore.javax.net.ssl;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
@@ -26,15 +28,18 @@ import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
-import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -66,11 +71,23 @@ import libcore.io.IoUtils;
import libcore.io.Streams;
import libcore.java.security.StandardNames;
import libcore.java.security.TestKeyStore;
+import libcore.tlswire.handshake.CipherSuite;
+import libcore.tlswire.handshake.ClientHello;
+import libcore.tlswire.handshake.CompressionMethod;
+import libcore.tlswire.handshake.HandshakeMessage;
+import libcore.tlswire.handshake.HelloExtension;
+import libcore.tlswire.handshake.ServerNameHelloExtension;
+import libcore.tlswire.record.TlsProtocols;
+import libcore.tlswire.record.TlsRecord;
+import libcore.tlswire.util.TlsProtocolVersion;
+import tests.util.ForEachRunner;
+import tests.util.DelegatingSSLSocketFactory;
+import tests.util.Pair;
public class SSLSocketTest extends TestCase {
public void test_SSLSocket_defaultConfiguration() throws Exception {
- SSLDefaultConfigurationAsserts.assertSSLSocket(
+ SSLConfigurationAsserts.assertSSLSocketDefaultConfiguration(
(SSLSocket) SSLSocketFactory.getDefault().createSocket());
}
@@ -1458,11 +1475,147 @@ public class SSLSocketTest extends TestCase {
test.close();
}
- public void test_SSLSocket_ClientHello_size() throws Exception {
+ public void test_SSLSocket_ClientHello_record_size() throws Exception {
// This test checks the size of ClientHello of the default SSLSocket. TLS/SSL handshakes
// with older/unpatched F5/BIG-IP appliances are known to stall and time out when
// the fragment containing ClientHello is between 256 and 511 (inclusive) bytes long.
- //
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, null, null);
+ SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+ sslSocketFactory = new DelegatingSSLSocketFactory(sslSocketFactory) {
+ @Override
+ protected void configureSocket(SSLSocket socket) {
+ // Enable SNI extension on the socket (this is typically enabled by default)
+ // to increase the size of ClientHello.
+ try {
+ Method setHostname =
+ socket.getClass().getMethod("setHostname", String.class);
+ setHostname.invoke(socket, "sslsockettest.androidcts.google.com");
+ } catch (NoSuchMethodException ignored) {
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to enable SNI", e);
+ }
+
+ // Enable Session Tickets extension on the socket (this is typically enabled
+ // by default) to increase the size of ClientHello.
+ try {
+ Method setUseSessionTickets =
+ socket.getClass().getMethod(
+ "setUseSessionTickets", boolean.class);
+ setUseSessionTickets.invoke(socket, true);
+ } catch (NoSuchMethodException ignored) {
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to enable Session Tickets", e);
+ }
+ }
+ };
+
+ TlsRecord firstReceivedTlsRecord = captureTlsHandshakeFirstTlsRecord(sslSocketFactory);
+ assertEquals("TLS record type", TlsProtocols.HANDSHAKE, firstReceivedTlsRecord.type);
+ HandshakeMessage handshakeMessage = HandshakeMessage.read(
+ new DataInputStream(new ByteArrayInputStream(firstReceivedTlsRecord.fragment)));
+ assertEquals("HandshakeMessage type",
+ HandshakeMessage.TYPE_CLIENT_HELLO, handshakeMessage.type);
+ int fragmentLength = firstReceivedTlsRecord.fragment.length;
+ if ((fragmentLength >= 256) && (fragmentLength <= 511)) {
+ fail("Fragment containing ClientHello is of dangerous length: "
+ + fragmentLength + " bytes");
+ }
+ }
+
+ public void test_SSLSocket_ClientHello_cipherSuites() throws Exception {
+ ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+ @Override
+ public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+ ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+ String[] cipherSuites = new String[clientHello.cipherSuites.size()];
+ for (int i = 0; i < clientHello.cipherSuites.size(); i++) {
+ CipherSuite cipherSuite = clientHello.cipherSuites.get(i);
+ cipherSuites[i] = cipherSuite.getAndroidName();
+ }
+ StandardNames.assertDefaultCipherSuites(cipherSuites);
+ }
+ }, getSSLSocketFactoriesToTest());
+ }
+
+ public void test_SSLSocket_ClientHello_clientProtocolVersion() throws Exception {
+ ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+ @Override
+ public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+ ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+ assertEquals(TlsProtocolVersion.TLSv1_2, clientHello.clientVersion);
+ }
+ }, getSSLSocketFactoriesToTest());
+ }
+
+ public void test_SSLSocket_ClientHello_compressionMethods() throws Exception {
+ ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+ @Override
+ public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+ ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+ assertEquals(Arrays.asList(CompressionMethod.NULL), clientHello.compressionMethods);
+ }
+ }, getSSLSocketFactoriesToTest());
+ }
+
+ public void test_SSLSocket_ClientHello_SNI() throws Exception {
+ ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+ @Override
+ public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+ ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+ ServerNameHelloExtension sniExtension = (ServerNameHelloExtension)
+ clientHello.findExtensionByType(HelloExtension.TYPE_SERVER_NAME);
+ assertNotNull(sniExtension);
+ assertEquals(Arrays.asList("localhost.localdomain"), sniExtension.hostnames);
+ }
+ }, getSSLSocketFactoriesToTest());
+ }
+
+ private List<Pair<String, SSLSocketFactory>> getSSLSocketFactoriesToTest()
+ throws NoSuchAlgorithmException, KeyManagementException {
+ List<Pair<String, SSLSocketFactory>> result =
+ new ArrayList<Pair<String, SSLSocketFactory>>();
+ result.add(Pair.of("default", (SSLSocketFactory) SSLSocketFactory.getDefault()));
+ for (String sslContextProtocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+ SSLContext sslContext = SSLContext.getInstance(sslContextProtocol);
+ if (StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT.equals(sslContextProtocol)) {
+ continue;
+ }
+ sslContext.init(null, null, null);
+ result.add(Pair.of(
+ "SSLContext(\"" + sslContext.getProtocol() + "\")",
+ sslContext.getSocketFactory()));
+ }
+ return result;
+ }
+
+ private ClientHello captureTlsHandshakeClientHello(SSLSocketFactory sslSocketFactory)
+ throws Exception {
+ TlsRecord record = captureTlsHandshakeFirstTlsRecord(sslSocketFactory);
+ assertEquals("TLS record type", TlsProtocols.HANDSHAKE, record.type);
+ ByteArrayInputStream fragmentIn = new ByteArrayInputStream(record.fragment);
+ HandshakeMessage handshakeMessage = HandshakeMessage.read(new DataInputStream(fragmentIn));
+ assertEquals("HandshakeMessage type",
+ HandshakeMessage.TYPE_CLIENT_HELLO, handshakeMessage.type);
+ // Assert that the fragment does not contain any more messages
+ assertEquals(0, fragmentIn.available());
+
+ return (ClientHello) handshakeMessage;
+ }
+
+ private TlsRecord captureTlsHandshakeFirstTlsRecord(SSLSocketFactory sslSocketFactory)
+ throws Exception {
+ byte[] firstReceivedChunk = captureTlsHandshakeFirstTransmittedChunkBytes(sslSocketFactory);
+ ByteArrayInputStream firstReceivedChunkIn = new ByteArrayInputStream(firstReceivedChunk);
+ TlsRecord record = TlsRecord.read(new DataInputStream(firstReceivedChunkIn));
+ // Assert that the chunk does not contain any more data
+ assertEquals(0, firstReceivedChunkIn.available());
+
+ return record;
+ }
+
+ private byte[] captureTlsHandshakeFirstTransmittedChunkBytes(
+ final SSLSocketFactory sslSocketFactory) throws Exception {
// Since there's no straightforward way to obtain a ClientHello from SSLSocket, this test
// does the following:
// 1. Creates a listening server socket (a plain one rather than a TLS/SSL one).
@@ -1506,33 +1659,19 @@ public class SSLSocketTest extends TestCase {
executorService.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(null, null, null);
- SSLSocket client = (SSLSocket) sslContext.getSocketFactory().createSocket();
+ Socket client = new Socket();
sockets[0] = client;
try {
- // Enable SNI extension on the socket (this is typically enabled by default)
- // to increase the size of ClientHello.
- try {
- Method setHostname =
- client.getClass().getMethod("setHostname", String.class);
- setHostname.invoke(client, "sslsockettest.androidcts.google.com");
- } catch (NoSuchMethodException ignored) {}
-
- // Enable Session Tickets extension on the socket (this is typically enabled
- // by default) to increase the size of ClientHello.
- try {
- Method setUseSessionTickets =
- client.getClass().getMethod(
- "setUseSessionTickets", boolean.class);
- setUseSessionTickets.invoke(client, true);
- } catch (NoSuchMethodException ignored) {}
-
client.connect(finalListeningSocket.getLocalSocketAddress());
// Initiate the TLS/SSL handshake which is expected to fail as soon as the
// server socket receives a ClientHello.
try {
- client.startHandshake();
+ SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
+ client,
+ "localhost.localdomain",
+ finalListeningSocket.getLocalPort(),
+ true);
+ sslSocket.startHandshake();
fail();
return null;
} catch (IOException expected) {}
@@ -1551,16 +1690,7 @@ public class SSLSocketTest extends TestCase {
});
// Wait for the ClientHello to arrive
- byte[] clientHello = readFirstReceivedChunkFuture.get(10, TimeUnit.SECONDS);
-
- // Check for ClientHello length that may cause handshake to fail/time out with older
- // F5/BIG-IP appliances.
- assertEquals("TLS record type: handshake", 22, clientHello[0]);
- int fragmentLength = ((clientHello[3] & 0xff) << 8) | (clientHello[4] & 0xff);
- if ((fragmentLength >= 256) && (fragmentLength <= 511)) {
- fail("Fragment containing ClientHello is of dangerous length: "
- + fragmentLength + " bytes");
- }
+ return readFirstReceivedChunkFuture.get(10, TimeUnit.SECONDS);
} finally {
executorService.shutdownNow();
IoUtils.closeQuietly(listeningSocket);
diff --git a/luni/src/test/java/libcore/net/MimeUtilsTest.java b/luni/src/test/java/libcore/net/MimeUtilsTest.java
index 9bfb375..ff22632 100644
--- a/luni/src/test/java/libcore/net/MimeUtilsTest.java
+++ b/luni/src/test/java/libcore/net/MimeUtilsTest.java
@@ -27,6 +27,12 @@ public class MimeUtilsTest extends TestCase {
assertEquals("flac", MimeUtils.guessExtensionFromMimeType("application/x-flac"));
}
+ // https://code.google.com/p/android/issues/detail?id=78909
+ public void test_78909() {
+ assertEquals("mka", MimeUtils.guessExtensionFromMimeType("audio/x-matroska"));
+ assertEquals("mkv", MimeUtils.guessExtensionFromMimeType("video/x-matroska"));
+ }
+
public void test_16978217() {
assertEquals("image/x-ms-bmp", MimeUtils.guessMimeTypeFromExtension("bmp"));
assertEquals("image/x-icon", MimeUtils.guessMimeTypeFromExtension("ico"));
diff --git a/luni/src/test/java/libcore/net/NetworkSecurityPolicyTest.java b/luni/src/test/java/libcore/net/NetworkSecurityPolicyTest.java
new file mode 100644
index 0000000..d04e2ba
--- /dev/null
+++ b/luni/src/test/java/libcore/net/NetworkSecurityPolicyTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.net;
+
+import junit.framework.TestCase;
+import libcore.io.IoUtils;
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.ErrorManager;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.SocketHandler;
+
+public class NetworkSecurityPolicyTest extends TestCase {
+
+ private boolean mCleartextTrafficPermittedOriginalState;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mCleartextTrafficPermittedOriginalState =
+ NetworkSecurityPolicy.isCleartextTrafficPermitted();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ try {
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(
+ mCleartextTrafficPermittedOriginalState);
+ } finally {
+ super.tearDown();
+ }
+ }
+
+ public void testCleartextTrafficPolicySetterAndGetter() {
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+ assertEquals(false, NetworkSecurityPolicy.isCleartextTrafficPermitted());
+
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+ assertEquals(true, NetworkSecurityPolicy.isCleartextTrafficPermitted());
+
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+ assertEquals(false, NetworkSecurityPolicy.isCleartextTrafficPermitted());
+
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+ assertEquals(true, NetworkSecurityPolicy.isCleartextTrafficPermitted());
+ }
+
+ public void testCleartextTrafficPolicyWithHttpURLConnection() throws Exception {
+ // Assert that client transmits some data when cleartext traffic is permitted.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+ try (CapturingServerSocket server = new CapturingServerSocket()) {
+ URL url = new URL("http://localhost:" + server.getPort() + "/test.txt");
+ try {
+ url.openConnection().getContent();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertDataTransmittedByClient();
+ }
+
+ // Assert that client does not transmit any data when cleartext traffic is not permitted and
+ // that URLConnection.openConnection or getContent fail with an IOException.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+ try (CapturingServerSocket server = new CapturingServerSocket()) {
+ URL url = new URL("http://localhost:" + server.getPort() + "/test.txt");
+ try {
+ url.openConnection().getContent();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertNoDataTransmittedByClient();
+ }
+ }
+
+ public void testCleartextTrafficPolicyWithFtpURLConnection() throws Exception {
+ // Assert that client transmits some data when cleartext traffic is permitted.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+ byte[] serverReplyOnConnect = "220\r\n".getBytes("US-ASCII");
+ try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
+ URL url = new URL("ftp://localhost:" + server.getPort() + "/test.txt");
+ try {
+ url.openConnection().getContent();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertDataTransmittedByClient();
+ }
+
+ // Assert that client does not transmit any data when cleartext traffic is not permitted and
+ // that URLConnection.openConnection or getContent fail with an IOException.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+ try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
+ URL url = new URL("ftp://localhost:" + server.getPort() + "/test.txt");
+ try {
+ url.openConnection().getContent();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertNoDataTransmittedByClient();
+ }
+ }
+
+ public void testCleartextTrafficPolicyWithJarHttpURLConnection() throws Exception {
+ // Assert that client transmits some data when cleartext traffic is permitted.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+ try (CapturingServerSocket server = new CapturingServerSocket()) {
+ URL url = new URL("jar:http://localhost:" + server.getPort() + "/test.jar!/");
+ try {
+ ((JarURLConnection) url.openConnection()).getManifest();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertDataTransmittedByClient();
+ }
+
+ // Assert that client does not transmit any data when cleartext traffic is not permitted and
+ // that JarURLConnection.openConnection or getManifest fail with an IOException.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+ try (CapturingServerSocket server = new CapturingServerSocket()) {
+ URL url = new URL("jar:http://localhost:" + server.getPort() + "/test.jar!/");
+ try {
+ ((JarURLConnection) url.openConnection()).getManifest();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertNoDataTransmittedByClient();
+ }
+ }
+
+ public void testCleartextTrafficPolicyWithJarFtpURLConnection() throws Exception {
+ // Assert that client transmits some data when cleartext traffic is permitted.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+ byte[] serverReplyOnConnect = "220\r\n".getBytes("US-ASCII");
+ try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
+ URL url = new URL("jar:ftp://localhost:" + server.getPort() + "/test.jar!/");
+ try {
+ ((JarURLConnection) url.openConnection()).getManifest();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertDataTransmittedByClient();
+ }
+
+ // Assert that client does not transmit any data when cleartext traffic is not permitted and
+ // that JarURLConnection.openConnection or getManifest fail with an IOException.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+ try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
+ URL url = new URL("jar:ftp://localhost:" + server.getPort() + "/test.jar!/");
+ try {
+ ((JarURLConnection) url.openConnection()).getManifest();
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertNoDataTransmittedByClient();
+ }
+ }
+
+ public void testCleartextTrafficPolicyWithLoggingSocketHandler() throws Exception {
+ // Assert that client transmits some data when cleartext traffic is permitted.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(true);
+ try (CapturingServerSocket server = new CapturingServerSocket()) {
+ SocketHandler logger = new SocketHandler("localhost", server.getPort());
+ MockErrorManager mockErrorManager = new MockErrorManager();
+ logger.setErrorManager(mockErrorManager);
+ logger.setLevel(Level.ALL);
+ LogRecord record = new LogRecord(Level.INFO, "A log record");
+ assertTrue(logger.isLoggable(record));
+ logger.publish(record);
+ assertNull(mockErrorManager.getMostRecentException());
+ server.assertDataTransmittedByClient();
+ }
+
+ // Assert that client does not transmit any data when cleartext traffic is not permitted.
+ NetworkSecurityPolicy.setCleartextTrafficPermitted(false);
+ try (CapturingServerSocket server = new CapturingServerSocket()) {
+ try {
+ new SocketHandler("localhost", server.getPort());
+ fail();
+ } catch (IOException expected) {
+ }
+ server.assertNoDataTransmittedByClient();
+ }
+ }
+
+ /**
+ * Server socket which listens on a local port and captures the first chunk of data transmitted
+ * by the client.
+ */
+ private static class CapturingServerSocket implements Closeable {
+ private final ServerSocket mSocket;
+ private final int mPort;
+ private final Thread mListeningThread;
+ private final FutureTask<byte[]> mFirstChunkReceivedFuture;
+
+ /**
+ * Constructs a new socket listening on a local port.
+ */
+ public CapturingServerSocket() throws IOException {
+ this(null);
+ }
+
+ /**
+ * Constructs a new socket listening on a local port, which sends the provided reply as
+ * soon as a client connects to it.
+ */
+ public CapturingServerSocket(final byte[] replyOnConnect) throws IOException {
+ mSocket = new ServerSocket(0);
+ mPort = mSocket.getLocalPort();
+ mFirstChunkReceivedFuture = new FutureTask<byte[]>(new Callable<byte[]>() {
+ @Override
+ public byte[] call() throws Exception {
+ try (Socket client = mSocket.accept()) {
+ // Reply (if requested)
+ if (replyOnConnect != null) {
+ client.getOutputStream().write(replyOnConnect);
+ client.getOutputStream().flush();
+ }
+
+ // Read request
+ byte[] buf = new byte[64 * 1024];
+ int chunkSize = client.getInputStream().read(buf);
+ if (chunkSize == -1) {
+ // Connection closed without any data received
+ return new byte[0];
+ }
+ // Received some data
+ return Arrays.copyOf(buf, chunkSize);
+ } finally {
+ IoUtils.closeQuietly(mSocket);
+ }
+ }
+ });
+ mListeningThread = new Thread(mFirstChunkReceivedFuture);
+ mListeningThread.start();
+ }
+
+ public int getPort() {
+ return mPort;
+ }
+
+ public Future<byte[]> getFirstReceivedChunkFuture() {
+ return mFirstChunkReceivedFuture;
+ }
+
+ @Override
+ public void close() {
+ IoUtils.closeQuietly(mSocket);
+ mListeningThread.interrupt();
+ }
+
+ private void assertDataTransmittedByClient()
+ throws Exception {
+ byte[] firstChunkFromClient = getFirstReceivedChunkFuture().get(2, TimeUnit.SECONDS);
+ if ((firstChunkFromClient == null) || (firstChunkFromClient.length == 0)) {
+ fail("Client did not transmit any data to server");
+ }
+ }
+
+ private void assertNoDataTransmittedByClient()
+ throws Exception {
+ byte[] firstChunkFromClient;
+ try {
+ firstChunkFromClient = getFirstReceivedChunkFuture().get(2, TimeUnit.SECONDS);
+ } catch (TimeoutException expected) {
+ return;
+ }
+ if ((firstChunkFromClient != null) && (firstChunkFromClient.length > 0)) {
+ fail("Client transmitted " + firstChunkFromClient.length+ " bytes: "
+ + new String(firstChunkFromClient, "US-ASCII"));
+ }
+ }
+ }
+
+ private static class MockErrorManager extends ErrorManager {
+ private Exception mMostRecentException;
+
+ public Exception getMostRecentException() {
+ synchronized (this) {
+ return mMostRecentException;
+ }
+ }
+
+ @Override
+ public void error(String message, Exception exception, int errorCode) {
+ synchronized (this) {
+ mMostRecentException = exception;
+ }
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/net/http/ResponseUtilsTest.java b/luni/src/test/java/libcore/net/http/ResponseUtilsTest.java
new file mode 100644
index 0000000..16745ee
--- /dev/null
+++ b/luni/src/test/java/libcore/net/http/ResponseUtilsTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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 libcore.net.http;
+
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.UnsupportedCharsetException;
+import junit.framework.TestCase;
+import static libcore.net.http.ResponseUtils.responseCharset;
+
+public class ResponseUtilsTest extends TestCase {
+ public void test_responseCharset_missing() {
+ assertEquals(StandardCharsets.UTF_8, responseCharset(null));
+ assertEquals(StandardCharsets.UTF_8, responseCharset("text/plain"));
+ assertEquals(StandardCharsets.UTF_8, responseCharset("text/plain;foo=bar;baz=bal"));
+ assertEquals(StandardCharsets.UTF_8, responseCharset("text/plain;charset="));
+ }
+
+ public void test_responseCharset_valid() {
+ assertEquals(StandardCharsets.ISO_8859_1,
+ responseCharset("text/plain;charset=ISO-8859-1"));
+ assertEquals(StandardCharsets.ISO_8859_1,
+ responseCharset("text/plain;CHARSET=ISO-8859-1"));
+ assertEquals(StandardCharsets.ISO_8859_1,
+ responseCharset("text/plain; charset = ISO-8859-1"));
+ assertEquals(StandardCharsets.ISO_8859_1,
+ responseCharset("text/plain; foo=bar;baz=bag;charset=ISO-8859-1"));
+ assertEquals(StandardCharsets.ISO_8859_1,
+ responseCharset("text/plain;charset=ISO-8859-1;;==,=="));
+ }
+
+ public void test_responseCharset_invalid() {
+ try {
+ responseCharset("text/plain;charset=unsupportedCharset");
+ fail();
+ } catch (UnsupportedCharsetException expected) {
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/util/HexEncodingTest.java b/luni/src/test/java/libcore/util/HexEncodingTest.java
new file mode 100644
index 0000000..ef79f5c
--- /dev/null
+++ b/luni/src/test/java/libcore/util/HexEncodingTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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 libcore.util;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import junit.framework.TestCase;
+import static libcore.util.HexEncoding.decode;
+import static libcore.util.HexEncoding.encode;
+
+public class HexEncodingTest extends TestCase {
+ public void testEncode() {
+ final byte[] avocados = "avocados".getBytes(StandardCharsets.UTF_8);
+
+ assertArraysEqual("61766f6361646f73".toCharArray(), encode(avocados));
+ assertArraysEqual(avocados, decode(encode(avocados), false));
+ }
+
+ public void testDecode_allow4Bit() {
+ assertArraysEqual(new byte[] { 6 }, decode("6".toCharArray(), true));
+ assertArraysEqual(new byte[] { 6, 'v' }, decode("676".toCharArray(), true));
+ }
+
+ public void testDecode_disallow4Bit() {
+ try {
+ decode("676".toCharArray(), false);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testDecode_invalid() {
+ try {
+ decode("deadbard".toCharArray(), false);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // This demonstrates a difference in behaviour from apache commons : apache
+ // commons uses Character.isDigit and would successfully decode a string with
+ // arabic and devanagari characters.
+ try {
+ decode("६१٧٥٥f6361646f73".toCharArray(), false);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ decode("#%6361646f73".toCharArray(), false);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ private static void assertArraysEqual(char[] lhs, char[] rhs) {
+ assertEquals(new String(lhs), new String(rhs));
+ }
+
+ private static void assertArraysEqual(byte[] lhs, byte[] rhs) {
+ assertEquals(Arrays.toString(lhs), Arrays.toString(rhs));
+ }
+}
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java
index 87b2913..17fb127 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java
@@ -28,7 +28,6 @@ import java.security.Provider;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
-import java.util.Vector;
import javax.crypto.ExemptionMechanism;
import javax.crypto.ExemptionMechanismException;
@@ -37,7 +36,6 @@ import javax.crypto.KeyGenerator;
import javax.crypto.ShortBufferException;
import org.apache.harmony.crypto.tests.support.MyExemptionMechanismSpi;
-import org.apache.harmony.crypto.tests.support.MyExemptionMechanismSpi.tmpKey;
import org.apache.harmony.security.tests.support.SpiEngUtils;
import junit.framework.TestCase;
@@ -170,45 +168,6 @@ public class ExemptionMechanismTest extends TestCase {
em.genExemptionBlob(new byte[10], -5);
}
- static boolean flag = false;
-
- class Mock_ExemptionMechanism extends ExemptionMechanism {
- protected Mock_ExemptionMechanism(ExemptionMechanismSpi exmechSpi, Provider provider, String mechanism) {
- super(exmechSpi, provider, mechanism);
- }
-
- @Override
- protected void finalize() {
- flag = true;
- super.finalize();
- }
- }
-
- // Side Effect: Causes OutOfMemoryError to test finalization
- public void test_finalize () {
- Mock_ExemptionMechanism mem = new Mock_ExemptionMechanism(null, null, "Name");
- assertNotNull(mem);
- mem = null;
- assertFalse(flag);
- Vector v = new Vector();
- int capacity;
- try {
- while(true) {
- v.add(this);
- }
- } catch (OutOfMemoryError e) {
- capacity = v.size();
- v = null;
- }
-
- v = new Vector();
- for (int i = 0; i < capacity/2; i++) {
- v.add(this);
- }
- v = null;
- assertTrue(flag);
- }
-
class Mock_ExemptionMechanismSpi extends MyExemptionMechanismSpi {
@Override
protected byte[] engineGenExemptionBlob()
diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java
index ddd0695..e90452d 100644
--- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java
+++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java
@@ -100,6 +100,12 @@ public class MacTest extends TestCase {
macList.add(Mac.getInstance(defaultAlgorithm, defaultProvider));
macList.add(Mac.getInstance(defaultAlgorithm, defaultProviderName));
for (Provider p : Security.getProviders("Mac." + defaultAlgorithm)) {
+ // Do not test AndroidKeyStore's Mac. It cannot be initialized without providing an
+ // AndroidKeyStore-backed SecretKey instance. It's OKish not to test here because it's
+ // tested by cts/tests/test/keystore.
+ if ("AndroidKeyStore".equals(p.getName())) {
+ continue;
+ }
macList.add(Mac.getInstance(defaultAlgorithm, p));
}
return macList.toArray(new Mac[macList.size()]);
@@ -845,6 +851,13 @@ public class MacTest extends TestCase {
byte[] output = null;
byte[] output2 = null;
for (int i = 0; i < providers.length; i++) {
+ // Do not test AndroidKeyStore's Mac. It cannot be initialized without providing an
+ // AndroidKeyStore-backed SecretKey instance. It's OKish not to test here because it's
+ // tested by cts/tests/test/keystore.
+ if ("AndroidKeyStore".equals(providers[i].getName())) {
+ continue;
+ }
+
System.out.println("provider = " + providers[i].getName());
Mac mac = Mac.getInstance("HmacMD5", providers[i]);
mac.init(key);
@@ -884,6 +897,24 @@ public class MacTest extends TestCase {
public abstract void setup();
}
+ public void testMac_getInstance_DoesNotSupportKeyClass_Success() throws Exception {
+ Provider mockProvider = new MockProvider("MockProvider") {
+ public void setup() {
+ put("Mac.FOO", MockMacSpi.AllKeyTypes.class.getName());
+ put("Mac.FOO SupportedKeyClasses", "None");
+ }
+ };
+
+ Security.addProvider(mockProvider);
+ try {
+ Mac s = Mac.getInstance("FOO", mockProvider);
+ s.init(new MockKey());
+ assertEquals(mockProvider, s.getProvider());
+ } finally {
+ Security.removeProvider(mockProvider.getName());
+ }
+ }
+
public void testMac_getInstance_SuppliedProviderNotRegistered_Success() throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
public void setup() {
diff --git a/luni/src/test/native/libcore_io_Memory_test.cpp b/luni/src/test/native/libcore_io_Memory_test.cpp
new file mode 100644
index 0000000..2d95155
--- /dev/null
+++ b/luni/src/test/native/libcore_io_Memory_test.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luni/src/main/native/libcore_io_Memory.cpp"
+
+#include <stdlib.h>
+
+#include <functional>
+
+#include <gtest/gtest.h>
+
+#define ALIGNMENT 8
+
+template<typename T, size_t NUM_ELEMENTS>
+void swap_align_test(void (*swap_func)(T*, const T*, size_t),
+ std::function<void (T*, T*, uint64_t)> init_func) {
+ uint8_t* dst = nullptr;
+ uint8_t* src = nullptr;
+ ASSERT_EQ(0, posix_memalign(reinterpret_cast<void**>(&dst), ALIGNMENT,
+ sizeof(T) * NUM_ELEMENTS + ALIGNMENT));
+ ASSERT_EQ(0, posix_memalign(reinterpret_cast<void**>(&src), ALIGNMENT,
+ sizeof(T) * NUM_ELEMENTS + ALIGNMENT));
+
+ T src_buf[NUM_ELEMENTS];
+ T dst_buf[NUM_ELEMENTS];
+ for (uint64_t i = 0; i < NUM_ELEMENTS; i++) {
+ init_func(&src_buf[i], &dst_buf[i], i);
+ }
+
+ // Vary a few alignments.
+ for (size_t dst_align = 0; dst_align < ALIGNMENT; dst_align++) {
+ T* dst_aligned = reinterpret_cast<T*>(&dst[dst_align]);
+ for (size_t src_align = 0; src_align < ALIGNMENT; src_align++) {
+ T* src_aligned = reinterpret_cast<T*>(&src[src_align]);
+ memset(dst_aligned, 0, sizeof(T) * NUM_ELEMENTS);
+ memcpy(src_aligned, src_buf, sizeof(T) * NUM_ELEMENTS);
+ swap_func(dst_aligned, src_aligned, NUM_ELEMENTS);
+ ASSERT_EQ(0, memcmp(dst_buf, dst_aligned, sizeof(T) * NUM_ELEMENTS))
+ << "Failed at dst align " << dst_align << " src align " << src_align;
+ }
+ }
+ free(dst);
+ free(src);
+}
+
+TEST(libcore, swapShorts_align_test) {
+ // Use an odd number to guarantee that the last 16-bit swap code
+ // is executed.
+ swap_align_test<jshort, 9> (swapShorts, [] (jshort* src, jshort* dst, uint64_t i) {
+ *src = ((2*i) << 8) | (2*(i+1));
+ *dst = (2*i) | ((2*(i+1)) << 8);
+ });
+}
+
+TEST(libcore, swapInts_align_test) {
+ swap_align_test<jint, 10> (swapInts, [] (jint* src, jint* dst, uint64_t i) {
+ *src = ((4*i) << 24) | ((4*(i+1)) << 16) | ((4*(i+2)) << 8) | (4*(i+3));
+ *dst = (4*i) | ((4*(i+1)) << 8) | ((4*(i+2)) << 16) | ((4*(i+3)) << 24);
+ });
+}
+
+TEST(libcore, swapLongs_align_test) {
+ swap_align_test<jlong, 10> (swapLongs, [] (jlong* src, jlong* dst, uint64_t i) {
+ *src = ((8*i) << 56) | ((8*(i+1)) << 48) | ((8*(i+2)) << 40) | ((8*(i+3)) << 32) |
+ ((8*(i+4)) << 24) | ((8*(i+5)) << 16) | ((8*(i+6)) << 8) | (8*(i+7));
+ *dst = (8*i) | ((8*(i+1)) << 8) | ((8*(i+2)) << 16) | ((8*(i+3)) << 24) |
+ ((8*(i+4)) << 32) | ((8*(i+5)) << 40) | ((8*(i+6)) << 48) | ((8*(i+7)) << 56);
+ });
+}
+
+template<typename T>
+void memory_peek_test(T (*peek_func)(JNIEnv*, jclass, jlong), T value) {
+ T* src = nullptr;
+ ASSERT_EQ(0, posix_memalign(reinterpret_cast<void**>(&src), ALIGNMENT,
+ sizeof(T) + ALIGNMENT));
+ for (size_t i = 0; i < ALIGNMENT; i++) {
+ jlong src_aligned = reinterpret_cast<jlong>(src) + i;
+ memcpy(reinterpret_cast<void*>(src_aligned), &value, sizeof(T));
+ T result = peek_func(nullptr, nullptr, src_aligned);
+ ASSERT_EQ(value, result);
+ }
+ free(src);
+}
+
+TEST(libcore, Memory_peekShortNative_align_check) {
+ memory_peek_test<jshort>(Memory_peekShortNative, 0x0102);
+}
+
+TEST(libcore, Memory_peekIntNative_align_check) {
+ memory_peek_test<jint>(Memory_peekIntNative, 0x01020304);
+}
+
+TEST(libcore, Memory_peekLongNative_align_check) {
+ memory_peek_test<jlong>(Memory_peekLongNative, 0x01020405060708ULL);
+}
+
+template<typename T>
+void memory_poke_test(void (*poke_func)(JNIEnv*, jclass, jlong, T), T value) {
+ T* dst = nullptr;
+ ASSERT_EQ(0, posix_memalign(reinterpret_cast<void**>(&dst), ALIGNMENT,
+ sizeof(T) + ALIGNMENT));
+ for(size_t i = 0; i < ALIGNMENT; i++) {
+ memset(dst, 0, sizeof(T) + ALIGNMENT);
+ jlong dst_aligned = reinterpret_cast<jlong>(dst) + i;
+ poke_func(nullptr, nullptr, dst_aligned, value);
+ ASSERT_EQ(0, memcmp(reinterpret_cast<void*>(dst_aligned), &value, sizeof(T)));
+ }
+ free(dst);
+}
+
+TEST(libcore, Memory_pokeShortNative_align_check) {
+ memory_poke_test<jshort>(Memory_pokeShortNative, 0x0102);
+}
+
+TEST(libcore, Memory_pokeIntNative_align_check) {
+ memory_poke_test<jint>(Memory_pokeIntNative, 0x01020304);
+}
+
+TEST(libcore, Memory_pokeLongNative_align_check) {
+ memory_poke_test<jlong>(Memory_pokeLongNative, 0x0102030405060708ULL);
+}
diff --git a/luni/src/test/native/test_openssl_engine.cpp b/luni/src/test/native/test_openssl_engine.cpp
deleted file mode 100644
index 9a0f3b3..0000000
--- a/luni/src/test/native/test_openssl_engine.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "UniquePtr.h"
-
-#include <stdarg.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <openssl/objects.h>
-#include <openssl/engine.h>
-#include <openssl/evp.h>
-#include <openssl/pem.h>
-
-#define DYNAMIC_ENGINE
-#define TEST_ENGINE_ID "javacoretests"
-#define TEST_ENGINE_NAME "libcore test engine"
-
-struct RSA_Delete {
- void operator()(RSA* p) const {
- RSA_free(p);
- }
-};
-typedef UniquePtr<RSA, RSA_Delete> Unique_RSA;
-
-static const char* HMAC_TAG = "-HMAC-";
-static const size_t HMAC_TAG_LEN = strlen(HMAC_TAG);
-
-static EVP_PKEY *test_load_key(ENGINE* e, const char *key_id,
- EVP_PKEY* (*read_func)(BIO*, EVP_PKEY**, pem_password_cb*, void*)) {
- void* data = static_cast<void*>(const_cast<char*>(key_id));
-
- EVP_PKEY *key = NULL;
-
- const size_t key_len = strlen(key_id);
- if (key_len > HMAC_TAG_LEN && !strncmp(key_id, HMAC_TAG, HMAC_TAG_LEN)) {
- key = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, e, reinterpret_cast<const unsigned char*>(key_id),
- key_len);
- } else {
- BIO* in = BIO_new_mem_buf(data, strlen(key_id));
- if (!in) {
- return NULL;
- }
- key = read_func(in, NULL, 0, NULL);
- BIO_free(in);
-
- if (key != NULL && EVP_PKEY_type(key->type) == EVP_PKEY_RSA) {
- ENGINE_init(e);
-
- Unique_RSA rsa(EVP_PKEY_get1_RSA(key));
- rsa->engine = e;
- rsa->flags |= RSA_FLAG_EXT_PKEY;
- }
- }
-
- return key;
-}
-
-static EVP_PKEY* test_load_privkey(ENGINE* e, const char* key_id, UI_METHOD*, void*) {
- return test_load_key(e, key_id, PEM_read_bio_PrivateKey);
-}
-
-static EVP_PKEY* test_load_pubkey(ENGINE* e, const char* key_id, UI_METHOD*, void*) {
- return test_load_key(e, key_id, PEM_read_bio_PUBKEY);
-}
-
-static const int meths[] = {
- EVP_PKEY_HMAC,
-};
-
-static int pkey_meths(ENGINE*, EVP_PKEY_METHOD** meth, const int** nids, int nid) {
- if (nid == EVP_PKEY_HMAC) {
- *meth = const_cast<EVP_PKEY_METHOD*>(EVP_PKEY_meth_find(nid));
- return 1;
- } else if (nid != 0) {
- return 0;
- }
-
- if (nids != NULL) {
- *nids = meths;
- return 1;
- }
-
- return 0;
-}
-
-static int test_engine_setup(ENGINE* e) {
- if (!ENGINE_set_id(e, TEST_ENGINE_ID)
- || !ENGINE_set_name(e, TEST_ENGINE_NAME)
- || !ENGINE_set_flags(e, 0)
- || !ENGINE_set_RSA(e, RSA_get_default_method())
- || !ENGINE_set_load_privkey_function(e, test_load_privkey)
- || !ENGINE_set_load_pubkey_function(e, test_load_pubkey)
- || !ENGINE_set_pkey_meths(e, pkey_meths)) {
- return 0;
- }
-
- return 1;
-}
-
-static int test_engine_bind_fn(ENGINE *e, const char *id) {
- if (id && (strcmp(id, TEST_ENGINE_ID) != 0)) {
- return 0;
- }
-
- if (!test_engine_setup(e)) {
- return 0;
- }
-
- return 1;
-}
-
-extern "C" {
-#undef OPENSSL_EXPORT
-#define OPENSSL_EXPORT extern __attribute__ ((visibility ("default")))
-
-IMPLEMENT_DYNAMIC_CHECK_FN()
-IMPLEMENT_DYNAMIC_BIND_FN(test_engine_bind_fn)
-};
diff --git a/luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider b/luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider
new file mode 100644
index 0000000..1a53312
--- /dev/null
+++ b/luni/src/test/resources/META-INF/services/java.nio.charset.spi.CharsetProvider
@@ -0,0 +1 @@
+libcore.java.nio.charset.SettableCharsetProvider