Merge "Port "Better ObjectIdentifier validation" change from openJdk8u121-b13"
diff --git a/dalvik/src/main/java/dalvik/annotation/SourceDebugExtension.java b/dalvik/src/main/java/dalvik/annotation/SourceDebugExtension.java
new file mode 100644
index 0000000..fe30adf
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/SourceDebugExtension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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 dalvik.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A "system annotation" used to provide the SourceDebugExtension attribute.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+@interface SourceDebugExtension {}
+
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
index 2a95450..6dd1ba6 100644
--- a/dalvik/src/main/java/dalvik/system/DexFile.java
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -29,12 +29,16 @@
import libcore.io.Libcore;
/**
- * Manipulates DEX files. The class is similar in principle to
- * {@link java.util.zip.ZipFile}. It is used primarily by class loaders.
- * <p>
- * Note we don't directly open and read the DEX file here. They're memory-mapped
- * read-only by the VM.
+ * Loads DEX files. This class is meant for internal use and should not be used
+ * by applications.
+ *
+ * @deprecated This class should not be used directly by applications. It will hurt
+ * performance in most cases and will lead to incorrect execution of bytecode in
+ * the worst case. Applications should use one of the standard classloaders such
+ * as {@link dalvik.system.PathClassLoader} instead. <b>This API will be removed
+ * in a future Android release</b>.
*/
+@Deprecated
public final class DexFile {
/**
* If close is called, mCookie becomes null but the internal cookie is preserved if the close
@@ -45,22 +49,13 @@
private final String mFileName;
/**
- * Opens a DEX file from a given File object. This will usually be a ZIP/JAR
- * file with a "classes.dex" inside.
+ * Opens a DEX file from a given File object.
*
- * The VM will generate the name of the corresponding file in
- * /data/dalvik-cache and open it, possibly creating or updating
- * it first if system permissions allow. Don't pass in the name of
- * a file in /data/dalvik-cache, as the named file is expected to be
- * in its original (pre-dexopt) state.
- *
- * @param file
- * the File object referencing the actual DEX file
- *
- * @throws IOException
- * if an I/O error occurs, such as the file not being found or
- * access rights missing for opening it
+ * @deprecated Applications should use one of the standard classloaders such
+ * as {@link dalvik.system.PathClassLoader} instead. <b>This API will be removed
+ * in a future Android release</b>.
*/
+ @Deprecated
public DexFile(File file) throws IOException {
this(file.getPath());
}
@@ -80,22 +75,13 @@
}
/**
- * Opens a DEX file from a given filename. This will usually be a ZIP/JAR
- * file with a "classes.dex" inside.
+ * Opens a DEX file from a given filename.
*
- * The VM will generate the name of the corresponding file in
- * /data/dalvik-cache and open it, possibly creating or updating
- * it first if system permissions allow. Don't pass in the name of
- * a file in /data/dalvik-cache, as the named file is expected to be
- * in its original (pre-dexopt) state.
- *
- * @param fileName
- * the filename of the DEX file
- *
- * @throws IOException
- * if an I/O error occurs, such as the file not being found or
- * access rights missing for opening it
+ * @deprecated Applications should use one of the standard classloaders such
+ * as {@link dalvik.system.PathClassLoader} instead. <b>This API will be removed
+ * in a future Android release</b>.
*/
+ @Deprecated
public DexFile(String fileName) throws IOException {
this(fileName, null, null);
}
@@ -165,24 +151,11 @@
* to be current, it will be used; if not, the VM will attempt to
* regenerate it.
*
- * This is intended for use by applications that wish to download
- * and execute DEX files outside the usual application installation
- * mechanism. This function should not be called directly by an
- * application; instead, use a class loader such as
- * dalvik.system.DexClassLoader.
- *
- * @param sourcePathName
- * Jar or APK file with "classes.dex". (May expand this to include
- * "raw DEX" in the future.)
- * @param outputPathName
- * File that will hold the optimized form of the DEX data.
- * @param flags
- * Enable optional features. (Currently none defined.)
- * @return
- * A new or previously-opened DexFile.
- * @throws IOException
- * If unable to open the source or output file.
+ * @deprecated Applications should use one of the standard classloaders such
+ * as {@link dalvik.system.PathClassLoader} instead. <b>This API will be removed
+ * in a future Android release</b>.
*/
+ @Deprecated
static public DexFile loadDex(String sourcePathName, String outputPathName,
int flags) throws IOException {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/PrintStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/PrintStreamTest.java
index 19a63af..601d522 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/PrintStreamTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/PrintStreamTest.java
@@ -19,6 +19,7 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.FilterOutputStream;
import java.io.InputStreamReader;
import java.io.DataInputStream;
import java.io.File;
@@ -27,9 +28,16 @@
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Base64;
import java.util.Locale;
+import libcore.io.IoUtils;
public class PrintStreamTest extends junit.framework.TestCase {
+ private static final String UNICODE_STRING =
+ "K\u03B1\u03BB\u03B7\u00B5\u03B5\u00B4\u03C1\u03B1 \u03BA\u03BF\u00B4\u03C3\u00B5\u03B5";
ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -70,19 +78,16 @@
* {@link java.io.PrintStream#PrintStream(String)}
*/
public void test_Constructor_Ljava_lang_String() throws IOException {
- MockPrintStream os = new MockPrintStream(testFilePath);
- assertNotNull(os);
+ PrintStream os = new PrintStream(testFilePath);
+ os.print(UNICODE_STRING);
os.close();
+ assertFileContents(UNICODE_STRING.getBytes(Charset.defaultCharset()), testFile);
}
/**
* {@link java.io.PrintStream#PrintStream(String, String)}
*/
public void test_Constructor_Ljava_lang_String_Ljava_lang_String() throws Exception {
- MockPrintStream os = new MockPrintStream(testFilePath, "utf-8");
- assertNotNull(os);
- os.close();
-
// Test that a bogus charset is mentioned in the exception
try {
new PrintStream(testFilePath, "Bogus");
@@ -90,6 +95,20 @@
} catch (UnsupportedEncodingException e) {
assertNotNull(e.getMessage());
}
+
+ {
+ PrintStream os = new PrintStream(testFilePath, "utf-8");
+ os.print(UNICODE_STRING);
+ os.close();
+ assertFileContents(UNICODE_STRING.getBytes(StandardCharsets.UTF_8), testFile);
+ }
+
+ {
+ PrintStream os = new PrintStream(testFilePath, "utf-16");
+ os.print(UNICODE_STRING);
+ os.close();
+ assertFileContents(UNICODE_STRING.getBytes(StandardCharsets.UTF_16), testFile);
+ }
}
/**
@@ -124,7 +143,7 @@
/**
* java.io.PrintStream#PrintStream(java.io.OutputStream, boolean, String)
*/
- public void test_ConstructorLjava_io_OutputStreamZLjava_lang_String() {
+ public void test_ConstructorLjava_io_OutputStreamZLjava_lang_String() throws Exception {
try {
new PrintStream(new ByteArrayOutputStream(), false,
"%Illegal_name!");
@@ -132,6 +151,24 @@
} catch (UnsupportedEncodingException e) {
// expected
}
+
+ {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ PrintStream printStream = new PrintStream(bos, true /* autoFlush */, "utf-8");
+ printStream.print(UNICODE_STRING);
+ printStream.close();
+ assertByteArraysEqual(UNICODE_STRING.getBytes(StandardCharsets.UTF_8),
+ bos.toByteArray());
+ }
+
+ {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ PrintStream printStream = new PrintStream(bos, true /* autoFlush */, "utf-16");
+ printStream.print(UNICODE_STRING);
+ printStream.close();
+ assertByteArraysEqual(UNICODE_STRING.getBytes(StandardCharsets.UTF_16),
+ bos.toByteArray());
+ }
}
/**
@@ -626,6 +663,59 @@
}
/**
+ * Tests that a PrintStream with {@code autoFlush == true} will call
+ * {@link OutputStream#flush()} at least once, after the last byte
+ * was written.
+ */
+ public void test_autoFlush_flushesEverything() {
+ CountFlushOutputStream counter = new CountFlushOutputStream(new ByteArrayOutputStream());
+ PrintStream printStream = new PrintStream(counter, true /* autoFlush */);
+ printStream.print("Hello, world!");
+ assertTrue(counter.hasBeenFlushedSinceLastWrite());
+ printStream.print(Math.PI);
+ assertTrue(counter.hasBeenFlushedSinceLastWrite());
+ printStream.print("\n");
+ assertTrue(counter.hasBeenFlushedSinceLastWrite());
+ printStream.println();
+ assertTrue(counter.hasBeenFlushedSinceLastWrite());
+ printStream.println("Lots\nof\nnewlines\n");
+ assertTrue(counter.hasBeenFlushedSinceLastWrite());
+ printStream.print("Line 1\nLine 2\n".toCharArray());
+ assertTrue(counter.hasBeenFlushedSinceLastWrite());
+
+ byte[] bytes = "Line without a newline".getBytes(StandardCharsets.UTF_8);
+ printStream.write(bytes, 0, bytes.length);
+ assertTrue(counter.hasBeenFlushedSinceLastWrite());
+ }
+
+ /**
+ * Tests that a PrintStream with {@code autoFlush == false} will not
+ * call {@link OutputStream#flush()} in regular (non-error) operation.
+ */
+ public void test_noAutoFlush() {
+ CountFlushOutputStream counter = new CountFlushOutputStream(new ByteArrayOutputStream());
+ PrintStream printStream = new PrintStream(counter, false /* autoFlush */);
+ printStream.print("Hello, world!");
+ printStream.print(Math.PI);
+ printStream.print("\n");
+ printStream.println();
+ printStream.println("Lots\nof\nnewlines\n");
+ printStream.print("Line 1\nLine 2\n".toCharArray());
+ byte[] bytes = "Line without a newline".getBytes(StandardCharsets.UTF_8);
+ printStream.write(bytes, 0, bytes.length);
+ assertFalse(counter.hasEverBeenFlushed());
+ assertFalse(counter.hasBeenFlushedSinceLastWrite());
+
+ // checkError() still causes the PrintStream to flush(), even when autoFlush == false.
+ printStream.checkError();
+ assertTrue(counter.hasEverBeenFlushed());
+ assertTrue(counter.hasBeenFlushedSinceLastWrite());
+ printStream.print("This data\nwill not be flushed.");
+ assertTrue(counter.hasEverBeenFlushed());
+ assertFalse(counter.hasBeenFlushedSinceLastWrite());
+ }
+
+ /**
* java.io.PrintStream#printf(java.lang.String, java.lang.Object...)
*/
public void test_printfLjava_lang_String$Ljava_lang_Object() {
@@ -669,5 +759,47 @@
super.tearDown();
}
+ private static void assertByteArraysEqual(byte[] expected, byte[] actual) {
+ String message = "Expected " + Base64.getEncoder().encodeToString(expected) + ", got: "
+ + Base64.getEncoder().encodeToString(actual);
+ assertTrue(message, Arrays.equals(actual, expected));
+ }
+
+ private static void assertFileContents(byte[] expected, File file) throws IOException {
+ byte[] actual = IoUtils.readFileAsByteArray(file.getAbsolutePath());
+ assertByteArraysEqual(expected, actual);
+ }
+
+ static class CountFlushOutputStream extends FilterOutputStream {
+ private boolean hasBeenFlushedSinceLastWrite = false;
+ private boolean hasEverBeenFlushed = false;
+
+ public CountFlushOutputStream(OutputStream delegate) {
+ super(delegate);
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ super.write(b);
+ hasBeenFlushedSinceLastWrite = false;
+ }
+
+ @Override
+ public void flush() throws IOException {
+ super.flush();
+ hasBeenFlushedSinceLastWrite = true;
+ hasEverBeenFlushed = true;
+ }
+
+ /** Whether {@link #flush()} has been called since the last write. */
+ public boolean hasBeenFlushedSinceLastWrite() {
+ return hasBeenFlushedSinceLastWrite;
+ }
+
+ /** Whether {@link #flush()} has ever been called after this stream was constructed. */
+ public boolean hasEverBeenFlushed() {
+ return hasEverBeenFlushed;
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/java/security/ProviderTest.java b/luni/src/test/java/libcore/java/security/ProviderTest.java
index ffba98a..d1eb3d4 100644
--- a/luni/src/test/java/libcore/java/security/ProviderTest.java
+++ b/luni/src/test/java/libcore/java/security/ProviderTest.java
@@ -277,6 +277,84 @@
}
}
+ /**
+ * Helper function to fetch services for Service.Algorithm IDs
+ */
+ private static Provider.Service getService(Provider p, String id) {
+ String[] typeAndAlg = id.split("\\.", 2);
+ assertEquals(id + " is not formatted as expected.", 2, typeAndAlg.length);
+ return p.getService(typeAndAlg[0], typeAndAlg[1]);
+ }
+
+ /**
+ * Ensures that, for all algorithms provided by Conscrypt, there is no alias from
+ * the BC provider that's not provided by Conscrypt. If there is, then a request
+ * for that alias with no provider specified will return the BC implementation of
+ * it even though we have a Conscrypt implementation available.
+ */
+ public void test_Provider_ConscryptOverridesBouncyCastle() throws Exception {
+ if (StandardNames.IS_RI) {
+ // These providers aren't installed on RI
+ return;
+ }
+ Provider conscrypt = Security.getProvider("AndroidOpenSSL");
+ Provider bc = Security.getProvider("BC");
+
+ // 1. Find all the algorithms provided by Conscrypt.
+ Set<String> conscryptAlgs = new HashSet<>();
+ for (Entry<Object, Object> entry : conscrypt.entrySet()) {
+ String key = (String) entry.getKey();
+ if (key.contains(" ")) {
+ // These are implementation properties like "Provider.id name"
+ continue;
+ }
+ if (key.startsWith("Alg.Alias.")) {
+ // Ignore aliases, we only want the concrete algorithms
+ continue;
+ }
+ conscryptAlgs.add(key);
+ }
+
+ // 2. Determine which classes in BC implement those algorithms
+ Set<String> bcClasses = new HashSet<>();
+ for (String conscryptAlg : conscryptAlgs) {
+ Provider.Service service = getService(bc, conscryptAlg);
+ if (service != null) {
+ bcClasses.add(service.getClassName());
+ }
+ }
+ assertTrue(bcClasses.size() > 0); // Sanity check
+
+ // 3. Determine which IDs in BC point to that set of classes
+ Set<String> shouldBeOverriddenBcIds = new HashSet<>();
+ for (Object keyObject : bc.keySet()) {
+ String key = (String) keyObject;
+ if (key.contains(" ")) {
+ continue;
+ }
+ if (key.startsWith("Alg.Alias.")) {
+ key = key.substring("Alg.Alias.".length());
+ }
+ Provider.Service service = getService(bc, key);
+ if (bcClasses.contains(service.getClassName())) {
+ shouldBeOverriddenBcIds.add(key);
+ }
+ }
+ assertTrue(shouldBeOverriddenBcIds.size() > 0); // Sanity check
+
+ // 4. Check each of those IDs to ensure that it's present in Conscrypt
+ Set<String> nonOverriddenIds = new TreeSet<>();
+ for (String shouldBeOverridenBcId : shouldBeOverriddenBcIds) {
+ if (getService(conscrypt, shouldBeOverridenBcId) == null) {
+ nonOverriddenIds.add(shouldBeOverridenBcId);
+ }
+ }
+ assertTrue("Conscrypt does not provide IDs " + nonOverriddenIds
+ + ", but it does provide other IDs that point to the same implementation(s)"
+ + " in BouncyCastle.",
+ nonOverriddenIds.isEmpty());
+ }
+
private static final String[] TYPES_SERVICES_CHECKED = new String[] {
"KeyFactory", "CertPathBuilder", "Cipher", "SecureRandom",
"AlgorithmParameterGenerator", "Signature", "KeyPairGenerator", "CertificateFactory",
diff --git a/ojluni/src/main/java/java/io/PrintStream.java b/ojluni/src/main/java/java/io/PrintStream.java
index 08bb3a3..809d39b 100644
--- a/ojluni/src/main/java/java/io/PrintStream.java
+++ b/ojluni/src/main/java/java/io/PrintStream.java
@@ -70,6 +70,7 @@
private BufferedWriter textOut;
private OutputStreamWriter charOut;
+ // Android-added: Lazy initialization of charOut and textOut.
private Charset charset;
/**
@@ -104,11 +105,18 @@
private PrintStream(boolean autoFlush, OutputStream out) {
super(out);
this.autoFlush = autoFlush;
+ // Android-changed: Lazy initialization of charOut and textOut.
+ // this.charOut = new OutputStreamWriter(this);
+ // this.textOut = new BufferedWriter(charOut);
}
private PrintStream(boolean autoFlush, OutputStream out, Charset charset) {
super(out);
this.autoFlush = autoFlush;
+ // Android-changed: Lazy initialization of charOut and textOut.
+ // this.charOut = new OutputStreamWriter(this, charset);
+ // this.textOut = new BufferedWriter(charOut);
+ this.charset = charset;
}
/* Variant of the private constructor so that the given charset name
@@ -344,7 +352,7 @@
private boolean closing = false; /* To avoid recursive closing */
- // Android-changed: Lazily initialize textOut.
+ // BEGIN Android-added: Lazy initialization of charOut and textOut.
private BufferedWriter getTextOut() {
if (textOut == null) {
charOut = charset != null ? new OutputStreamWriter(this, charset) :
@@ -353,6 +361,7 @@
}
return textOut;
}
+ // END Android-added: Lazy initialization of charOut and textOut.
/**
* Closes the stream. This is done by flushing the stream and then closing
@@ -365,10 +374,12 @@
if (! closing) {
closing = true;
try {
- // Android-changed: Lazily initialized.
+ // BEGIN Android-changed: Lazy initialization of charOut and textOut.
+ // textOut.close();
if (textOut != null) {
textOut.close();
}
+ // END Android-changed: Lazy initialization of charOut and textOut.
out.close();
}
catch (IOException x) {
@@ -512,7 +523,7 @@
try {
synchronized (this) {
ensureOpen();
- // Android-changed: Lazily initialized.
+ // Android-added: Lazy initialization of charOut and textOut.
BufferedWriter textOut = getTextOut();
textOut.write(buf);
textOut.flushBuffer();
@@ -536,7 +547,7 @@
try {
synchronized (this) {
ensureOpen();
- // Android-changed: Lazily initialized.
+ // Android-added: Lazy initialization of charOut and textOut.
BufferedWriter textOut = getTextOut();
textOut.write(s);
textOut.flushBuffer();
@@ -557,7 +568,7 @@
try {
synchronized (this) {
ensureOpen();
- // Android-changed: Lazily initialized.
+ // Android-added: Lazy initialization of charOut and textOut.
BufferedWriter textOut = getTextOut();
textOut.newLine();
textOut.flushBuffer();
diff --git a/ojluni/src/main/native/NativeThread.c b/ojluni/src/main/native/NativeThread.c
index f8a68e7..9147bc3 100644
--- a/ojluni/src/main/native/NativeThread.c
+++ b/ojluni/src/main/native/NativeThread.c
@@ -35,8 +35,10 @@
#ifdef __linux__
#include <pthread.h>
#include <sys/signal.h>
+ // Android-changed: Bionic (and AsynchronousCloseMonitor) expects libcore to use
+ // __SIGRTMIN + 2, not __SIGRTMAX - 2
/* Also defined in net/linux_close.c */
- #define INTERRUPT_SIGNAL (__SIGRTMAX - 2)
+ #define INTERRUPT_SIGNAL (__SIGRTMIN + 2)
#elif __solaris__
#include <thread.h>
#include <signal.h>
diff --git a/ojluni/src/main/native/linux_close.cpp b/ojluni/src/main/native/linux_close.cpp
index e9a2588..6f88c93 100644
--- a/ojluni/src/main/native/linux_close.cpp
+++ b/ojluni/src/main/native/linux_close.cpp
@@ -45,7 +45,9 @@
/*
* Signal to unblock thread
*/
-static int sigWakeup = (__SIGRTMAX - 2);
+// Android-changed: Bionic (and AsynchronousCloseMonitor) expects libcore to use
+// __SIGRTMIN + 2, not __SIGRTMAX - 2
+static int sigWakeup = (__SIGRTMIN + 2);
/*
* Close or dup2 a file descriptor ensuring that all threads blocked on