diff options
10 files changed, 221 insertions, 26 deletions
diff --git a/luni/src/main/java/java/net/NetworkInterface.java b/luni/src/main/java/java/net/NetworkInterface.java index 852c09b..0f8e37a 100644 --- a/luni/src/main/java/java/net/NetworkInterface.java +++ b/luni/src/main/java/java/net/NetworkInterface.java @@ -118,7 +118,12 @@ public final class NetworkInterface extends Object { */ private static NetworkInterface getByNameInternal(String interfaceName, String[] ifInet6Lines) throws SocketException { - int interfaceIndex = readIntFile("/sys/class/net/" + interfaceName + "/ifindex"); + int interfaceIndex; + try { + interfaceIndex = readIntFile("/sys/class/net/" + interfaceName + "/ifindex"); + } catch (SocketException e) { + return null; + } List<InetAddress> addresses = new ArrayList<InetAddress>(); List<InterfaceAddress> interfaceAddresses = new ArrayList<InterfaceAddress>(); diff --git a/luni/src/main/java/libcore/icu/TimeZoneNames.java b/luni/src/main/java/libcore/icu/TimeZoneNames.java index 3413a5d..139ab57 100644 --- a/luni/src/main/java/libcore/icu/TimeZoneNames.java +++ b/luni/src/main/java/libcore/icu/TimeZoneNames.java @@ -135,6 +135,18 @@ public final class TimeZoneNames { } /** + * Clear the cached zone strings for {@link Locale#getDefault()}. + * Does nothing if it returns {@link Locale#US} or {@link Locale#ROOT}. + */ + public static void clearLocaleCache() { + Locale locale = Locale.getDefault(); + if (locale.equals(Locale.US) || locale.equals(Locale.ROOT)) { + return; + } + cachedZoneStrings.remove(locale); + } + + /** * Returns an array containing the time zone ids in use in the country corresponding to * the given locale. This is not necessary for Java API, but is used by telephony as a * fallback. We retrieve these strings from zone.tab rather than icu4c because the latter diff --git a/luni/src/main/java/libcore/util/BasicLruCache.java b/luni/src/main/java/libcore/util/BasicLruCache.java index 75e4a75..00afa98 100644 --- a/luni/src/main/java/libcore/util/BasicLruCache.java +++ b/luni/src/main/java/libcore/util/BasicLruCache.java @@ -79,6 +79,13 @@ public class BasicLruCache<K, V> { return previous; } + public synchronized final V remove(K key) { + if (key == null) { + throw new NullPointerException("key == null"); + } + return map.remove(key); + } + private void trimToSize(int maxSize) { while (map.size() > maxSize) { Map.Entry<K, V> toEvict = map.eldest(); diff --git a/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java b/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java index 8708214..fce8507 100644 --- a/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java +++ b/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java @@ -19,6 +19,7 @@ package com.android.org.bouncycastle.crypto.digests; import junit.framework.TestCase; import com.android.org.bouncycastle.crypto.Digest; import com.android.org.bouncycastle.crypto.ExtendedDigest; +import tests.util.SummaryStatistics; /** * Implements unit tests for our JNI wrapper around OpenSSL. We use the @@ -36,6 +37,7 @@ public class DigestTest extends TestCase { * @param newDigest The new digest implementation, provided by OpenSSL */ public void doTestMessageDigest(Digest oldDigest, Digest newDigest) { + final int WARMUP = 10; final int ITERATIONS = 100; byte[] data = new byte[1024]; @@ -54,27 +56,31 @@ public class DigestTest extends TestCase { data[i] = (byte)i; } - long oldTime = 0; - long newTime = 0; + SummaryStatistics oldTime = new SummaryStatistics(); + SummaryStatistics newTime = new SummaryStatistics(); - for (int j = 0; j < ITERATIONS; j++) { - long t0 = System.currentTimeMillis(); + for (int j = 0; j < ITERATIONS + WARMUP; j++) { + long t0 = System.nanoTime(); for (int i = 0; i < 4; i++) { oldDigest.update(data, 0, data.length); } int oldLength = oldDigest.doFinal(oldHash, 0); - long t1 = System.currentTimeMillis(); + long t1 = System.nanoTime(); - oldTime = oldTime + (t1 - t0); + if (j >= WARMUP) { + oldTime.add(t1 - t0); + } - long t2 = System.currentTimeMillis(); + long t2 = System.nanoTime(); for (int i = 0; i < 4; i++) { newDigest.update(data, 0, data.length); } int newLength = newDigest.doFinal(newHash, 0); - long t3 = System.currentTimeMillis(); + long t3 = System.nanoTime(); - newTime = newTime + (t3 - t2); + if (j >= WARMUP) { + newTime.add(t3 - t2); + } assertEquals("Hash sizes must be equal", oldLength, newLength); @@ -83,10 +89,13 @@ public class DigestTest extends TestCase { } } - System.out.println("Time for " + ITERATIONS + " x old hash processing: " + oldTime + " ms"); - System.out.println("Time for " + ITERATIONS + " x new hash processing: " + newTime + " ms"); + System.out.println("Time for " + ITERATIONS + " x old hash processing: " + + oldTime.toString()); + System.out.println("Time for " + ITERATIONS + " x new hash processing: " + + newTime.toString()); - assertTrue("New hash should be faster", newTime < oldTime); + assertTrue("New hash should be faster:\nold=" + oldTime.toString() + "\nnew=" + + newTime.toString(), newTime.mean() < oldTime.mean()); } /** diff --git a/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java b/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java index ef303bd..d71b5b0 100644 --- a/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java +++ b/luni/src/test/java/libcore/java/lang/ref/FinalizeTest.java @@ -69,13 +69,18 @@ public final class FinalizeTest extends TestCase { static class X {} - // http://b/issue?id=2136462 - public void testBackFromTheDead() throws Exception { + // Helper function since we do not want a vreg to keep the allocated object live. + // For b/25851249 + private void exceptionInConstructor() { try { new ConstructionFails(); } catch (AssertionError expected) { } + } + // http://b/issue?id=2136462 + public void testBackFromTheDead() throws Exception { + exceptionInConstructor(); FinalizationTester.induceFinalization(); assertTrue("object whose constructor threw was not finalized", ConstructionFails.finalized); } diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java index a434d94..bc2b626 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSessionTest.java @@ -99,7 +99,7 @@ public class SSLSessionTest extends TestCase { assertTrue("s.server.getLastAccessedTime()=" + s.server.getLastAccessedTime() + " " + "s.client.getLastAccessedTime()=" + s.client.getLastAccessedTime(), Math.abs(s.server.getLastAccessedTime() - - s.client.getLastAccessedTime()) < 1 * 1000); + - s.client.getLastAccessedTime()) <= 1 * 1000); assertTrue(s.server.getLastAccessedTime() >= s.server.getCreationTime()); assertTrue(s.client.getLastAccessedTime() >= 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 bf2d0f8..11dfb3d 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java @@ -1559,7 +1559,23 @@ public class SSLSocketTest extends TestCase { @Override public void run(SSLSocketFactory sslSocketFactory) throws Exception { ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory); - String[] cipherSuites = new String[clientHello.cipherSuites.size()]; + final String[] cipherSuites; + + // RFC 5746 allows you to send an empty "renegotiation_info" extension *or* + // a special signaling cipher suite. The TLS API has no way to check or + // indicate that a certain TLS extension should be used. + HelloExtension renegotiationInfoExtension = clientHello.findExtensionByType( + HelloExtension.TYPE_RENEGOTIATION_INFO); + if (renegotiationInfoExtension != null && + renegotiationInfoExtension.data.length == 1 && + renegotiationInfoExtension.data[0] == 0) { + cipherSuites = new String[clientHello.cipherSuites.size() + 1]; + cipherSuites[clientHello.cipherSuites.size()] = + StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION; + } else { + 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(); diff --git a/support/src/test/java/libcore/tlswire/handshake/HelloExtension.java b/support/src/test/java/libcore/tlswire/handshake/HelloExtension.java index 5741072..a648cdf 100644 --- a/support/src/test/java/libcore/tlswire/handshake/HelloExtension.java +++ b/support/src/test/java/libcore/tlswire/handshake/HelloExtension.java @@ -31,6 +31,7 @@ public class HelloExtension { public static final int TYPE_SERVER_NAME = 0; public static final int TYPE_PADDING = 21; public static final int TYPE_SESSION_TICKET = 35; + public static final int TYPE_RENEGOTIATION_INFO = 65281; private static final Map<Integer, String> TYPE_TO_NAME = new HashMap<Integer, String>(); static { @@ -60,7 +61,7 @@ public class HelloExtension { TYPE_TO_NAME.put(13172, "next_protocol_negotiation"); TYPE_TO_NAME.put(30031, "Channel ID (old)"); TYPE_TO_NAME.put(30032, "Channel ID (new)"); - TYPE_TO_NAME.put(65281, "renegotiation_info"); + TYPE_TO_NAME.put(TYPE_RENEGOTIATION_INFO, "renegotiation_info"); } public int type; diff --git a/support/src/test/java/tests/util/SummaryStatistics.java b/support/src/test/java/tests/util/SummaryStatistics.java new file mode 100644 index 0000000..4ce0a04 --- /dev/null +++ b/support/src/test/java/tests/util/SummaryStatistics.java @@ -0,0 +1,82 @@ +/* + * 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 tests.util; + +public class SummaryStatistics { + /** The number of values seen. */ + private int numValues; + + /** Sum of the values. */ + private double sum; + + /** Sum of the squares of the values added. */ + private double squaresSum; + + /** The previously added value. */ + private double lastValue; + + public SummaryStatistics() { + } + + private double square(double value) { + return value * value; + } + + /** Add a new value to the values seen. */ + public void add(double value) { + sum += value - lastValue; + squaresSum += square(value) - square(lastValue); + numValues++; + lastValue = value; + } + + /** Mean of the values seen. */ + public double mean() { + return sum / numValues; + } + + /** Variance of the values seen. */ + public double var() { + return (squaresSum / numValues) - square(mean()); + } + + /** Standard deviation of the values seen. */ + public double stddev() { + return Math.sqrt(var()); + } + + /** Coefficient of variation of the values seen. */ + public double coeffVar() { + return stddev() / mean(); + } + + public String toString() { + StringBuilder sb = new StringBuilder("SummaryStatistics{"); + sb.append("n="); + sb.append(numValues); + sb.append(",mean="); + sb.append(mean()); + sb.append(",var="); + sb.append(var()); + sb.append(",stddev="); + sb.append(stddev()); + sb.append(",coeffVar="); + sb.append(coeffVar()); + sb.append('}'); + return sb.toString(); + } +} diff --git a/xml/src/main/java/org/kxml2/io/KXmlParser.java b/xml/src/main/java/org/kxml2/io/KXmlParser.java index a90db3b..a75cc72 100644 --- a/xml/src/main/java/org/kxml2/io/KXmlParser.java +++ b/xml/src/main/java/org/kxml2/io/KXmlParser.java @@ -157,6 +157,8 @@ public class KXmlParser implements XmlPullParser, Closeable { private boolean isWhitespace; private String namespace; private String prefix; + private String foundPrefix = null; + private String foundName = null; private String name; private String text; @@ -268,15 +270,21 @@ public class KXmlParser implements XmlPullParser, Closeable { } } - int cut = name.indexOf(':'); + if (foundPrefix != null && foundName != null) { + prefix = foundPrefix; + name = foundName; + } + else { + int cut = name.indexOf(':'); if (cut == 0) { checkRelaxed("illegal tag name: " + name); } - if (cut != -1) { - prefix = name.substring(0, cut); - name = name.substring(cut + 1); + if (cut != -1) { + prefix = name.substring(0, cut); + name = name.substring(cut + 1); + } } this.namespace = getNamespace(prefix); @@ -961,13 +969,19 @@ public class KXmlParser implements XmlPullParser, Closeable { } private void readEndTag() throws IOException, XmlPullParserException { + int sp = (depth - 1) * 4; read('<'); read('/'); - name = readName(); // TODO: pass the expected name in as a hint? + if (depth == 0) { + name = readName(); + } + else { + // Pass the expected name in as a hint. + name = readExpectedName(elementStack[sp + 3]); + } skip(); read('>'); - int sp = (depth - 1) * 4; if (depth == 0) { checkRelaxed("read end tag " + name + " with no tags open"); @@ -1055,7 +1069,7 @@ public class KXmlParser implements XmlPullParser, Closeable { if (!xmldecl) { read('<'); } - name = readName(); + name = readName(true); attributeCount = 0; while (true) { @@ -1429,7 +1443,12 @@ public class KXmlParser implements XmlPullParser, Closeable { } if (result == null) { - return stringPool.get(buffer, start, position - start); + if (isWhitespace) { + return stringPool.get(buffer, start, position - start); + } + else { + return new String(buffer, start, position - start); + } } else { result.append(buffer, start, position - start); return result.toString(); @@ -1525,13 +1544,44 @@ public class KXmlParser implements XmlPullParser, Closeable { * Returns an element or attribute name. This is always non-empty for * non-relaxed parsers. */ + private String readExpectedName(String expected) throws IOException, XmlPullParserException { + int length = expected.length(); + int end = position + length; + if (end < limit) { + // Fast path for normal case. + boolean match = true; + for (int i = 0; i < length; i++) { + if (buffer[position+i] != expected.charAt(i)) { + match = false; + break; + } + } + if (match) { + position += length; + return expected; + } + } + return readName(); + } + private String readName() throws IOException, XmlPullParserException { + return readName(false); + } + + /** + * Returns an element or attribute name. This is always non-empty for + * non-relaxed parsers. findPrefix should only be true for element names + */ + private String readName(boolean findPrefix) throws IOException, XmlPullParserException { if (position >= limit && !fillBuffer(1)) { checkRelaxed("name expected"); return ""; } int start = position; + int nameStart = -1; + foundPrefix = null; + foundName = null; StringBuilder result = null; // read the first character @@ -1575,12 +1625,20 @@ public class KXmlParser implements XmlPullParser, Closeable { || c == ':' || c == '.' || c >= '\u00b7') { // TODO: check the XML spec + // Fast path for common case + if (c == ':' && findPrefix && foundPrefix == null) { + foundPrefix = stringPool.get(buffer, start, position - start); + nameStart = position+1; + } position++; continue; } // we encountered a non-name character. done! if (result == null) { + if (foundPrefix != null && position != nameStart) { + foundName = stringPool.get(buffer, nameStart, position - nameStart); + } return stringPool.get(buffer, start, position - start); } else { result.append(buffer, start, position - start); |